Each finished broadcast is filed locally — tier, probed encoder, rated, settled, steps spent — and Models/RateHistory turns that into at most a suggestion. The app never applies one: it asks before the next stream, "Use it" sets the rate, "Keep mine" remembers that exact number so the same question does not return until the evidence moves. Nothing leaves the machine. Two rules keep it from nagging. The high-water mark is the highest rate that *finished* a session with zero steps, not the highest seen, so one easy session (a menu screen, a quiet game) cannot set a target the machine cannot hold. And it is asymmetric: raising needs 10% above the current rate across five clean sessions of the same tier and encoder, lowering needs three step-downs in five — fixing damage should not wait for a percentage, and risking it should. The encoder key is real, not decorative: StreamHealth.VideoEncoder is now filled from the probe (FfmpegEncoder), so history recorded against NVENC is not applied to a machine that later runs QSV. At startup, before anything has streamed, the last session on that tier is the honest guess. 672 pass, 0 warnings.
58 lines
1.7 KiB
C#
58 lines
1.7 KiB
C#
namespace ytLive.Models;
|
|
|
|
public enum StreamStatus
|
|
{
|
|
Offline,
|
|
Connecting,
|
|
Streaming,
|
|
Error
|
|
}
|
|
|
|
public class StreamConfig
|
|
{
|
|
public string Title { get; set; } = string.Empty;
|
|
public string Description { get; set; } = string.Empty;
|
|
public string StreamKey { get; set; } = string.Empty;
|
|
public string IngestUrl { get; set; } = "rtmp://a.rtmp.youtube.com/live2";
|
|
public bool IsLowLatency { get; set; } = true;
|
|
public int TargetBitrate { get; set; } = 6000;
|
|
public int TargetFps { get; set; } = 60;
|
|
public string Resolution { get; set; } = "1920x1080";
|
|
}
|
|
|
|
public class StreamHealth
|
|
{
|
|
public StreamStatus Status { get; set; }
|
|
public double CurrentBitrate { get; set; }
|
|
public int DroppedFrames { get; set; }
|
|
public double FPS { get; set; }
|
|
public TimeSpan StreamDuration { get; set; }
|
|
public string? LastError { get; set; }
|
|
|
|
/// <summary>The encoder the probe actually chose ("h264_nvenc", "libopenh264", …). Filled once the
|
|
/// encoder starts, and the key the rate history is filed under — evidence about NVENC says nothing
|
|
/// about QSV on another machine.</summary>
|
|
public string? VideoEncoder { get; set; }
|
|
|
|
// YouTube-specific (liveStreams.status)
|
|
public string? HealthStatus { get; set; } // "good", "ok", "bad", "noData"
|
|
public string? HealthMessage { get; set; }
|
|
|
|
// Report-by-exception (TASK 5 item 3): configurationIssues[] — the banner
|
|
// surfaces only warning/error entries.
|
|
public List<StreamConfigurationIssue> ConfigurationIssues { get; } = new();
|
|
}
|
|
|
|
public enum StreamIssueSeverity
|
|
{
|
|
Info,
|
|
Warning,
|
|
Error
|
|
}
|
|
|
|
public class StreamConfigurationIssue
|
|
{
|
|
public StreamIssueSeverity Severity { get; set; }
|
|
public string? Type { get; set; }
|
|
}
|