feat(entitlement): gate 60 fps output through Steam

This commit is contained in:
2026-11-04 12:14:46 -08:00
parent 1993e7cc8b
commit 7216c95449
12 changed files with 188 additions and 72 deletions
+6 -12
View File
@@ -3,11 +3,8 @@ namespace ytLive.Models;
/// <summary>
/// The one entitlement rule, as pure policy: what a build is allowed to emit.
///
/// <para><b>Why pixels and a frame rate rather than "720p".</b> The paid delta is output
/// quality, and a resolution name is a marketing word, not a measurable bound — 720p60 is
/// 921,600 pixels at twice the frame rate, and vertical 720×1280 is the same pixel count
/// with a different shape. A pixel budget plus a frame-rate ceiling expresses the same
/// promise without a lookup table that a new tier can quietly escape.</para>
/// <para><b>Frame rate is the paid boundary.</b> Every supported resolution at 30 fps is free;
/// 60 fps requires paid ownership, regardless of frame dimensions.</para>
///
/// <para><b>Fail closed.</b> Callers pass <c>pro: false</c> when entitlement is unknown,
/// unreadable or unresolved; the free profile is the default state, never the fallback
@@ -19,19 +16,16 @@ namespace ytLive.Models;
/// </summary>
public static class OutputProfile
{
/// <summary>Free ceiling: 921,600 px = 1280×720, i.e. one 720p frame.</summary>
public const int FreeMaxPixels = 921_600;
/// <summary>Free ceiling: 30 frames per second.</summary>
public const int FreeMaxFps = 30;
/// <summary>The named tier a capped build falls back to when nothing else qualifies.</summary>
public const string FreeFallbackName = "720p30";
/// <summary>The default free tier and fallback when no shipped tier is available.</summary>
public const string FreeFallbackName = "1080p30";
/// <summary>Is this tier within the free budget? Pro may use anything shipped.</summary>
public static bool IsAllowed(QualityOption tier, bool pro)
=> pro
|| (tier.Width * tier.Height <= FreeMaxPixels && tier.Fps <= FreeMaxFps);
|| tier.Fps <= FreeMaxFps;
/// <summary>
/// The tier a session will actually encode. A permitted selection passes through
@@ -57,6 +51,6 @@ public static class OutputProfile
// No shipped tier fits the budget: build the floor explicitly rather than trusting
// the table. A build whose tiers were all redefined must still emit a legal frame.
return candidate ?? new QualityOption(FreeFallbackName, FreeMaxFps, 6.0, 1280, 720);
return candidate ?? new QualityOption(FreeFallbackName, FreeMaxFps, 8.0, 1920, 1080);
}
}