133 lines
5.2 KiB
C#
133 lines
5.2 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using Paca.Core.Hls;
|
|
using Paca.Core.Platform;
|
|
using Xunit;
|
|
|
|
namespace Paca.Tests.Core;
|
|
|
|
public class AdaptiveStreamingBitrateControllerGateTests
|
|
{
|
|
private readonly List<Variant> _sampleVariants = new()
|
|
{
|
|
new Variant(new Uri("https://paca.stream/v_1080.m3u8"), 6_000_000, "1920x1080", "avc1"),
|
|
new Variant(new Uri("https://paca.stream/v_720.m3u8"), 2_800_000, "1280x720", "avc1"),
|
|
new Variant(new Uri("https://paca.stream/v_480.m3u8"), 1_200_000, "854x480", "avc1"),
|
|
new Variant(new Uri("https://paca.stream/v_360.m3u8"), 600_000, "640x360", "avc1")
|
|
};
|
|
|
|
[Fact]
|
|
public void Gate464_AdaptiveStreamingBitrateController_CalculatesEwmaSpeed_Accurately()
|
|
{
|
|
var controller = new AdaptiveStreamingBitrateController();
|
|
|
|
// 1MB in 1 sec = 8,000,000 bps
|
|
controller.RecordSample(bytes: 1_000_000, duration: TimeSpan.FromSeconds(1));
|
|
var speed1 = controller.CurrentBandwidthBps;
|
|
Assert.Equal(8_000_000, speed1);
|
|
|
|
// Next sample: 500KB in 1 sec = 4,000,000 bps
|
|
// EWMA with alpha 0.5: 0.5 * 4M + 0.5 * 8M = 6,000,000 bps
|
|
controller.RecordSample(bytes: 500_000, duration: TimeSpan.FromSeconds(1), alpha: 0.5);
|
|
var speed2 = controller.CurrentBandwidthBps;
|
|
Assert.Equal(6_000_000, speed2);
|
|
}
|
|
|
|
[Fact]
|
|
public void Gate465_AdaptiveStreamingBitrateController_SelectsHighestQuality_UnderHighestStrategy()
|
|
{
|
|
var controller = new AdaptiveStreamingBitrateController(BitrateSelectionStrategy.HighestQuality);
|
|
var selected = controller.SelectVariant(_sampleVariants);
|
|
|
|
Assert.NotNull(selected);
|
|
Assert.Equal(6_000_000, selected.Bandwidth);
|
|
Assert.Equal(1080, selected.Height);
|
|
}
|
|
|
|
[Fact]
|
|
public void Gate466_AdaptiveStreamingBitrateController_SelectsLowestBandwidth_UnderDataSaverStrategy()
|
|
{
|
|
var controller = new AdaptiveStreamingBitrateController(BitrateSelectionStrategy.DataSaver);
|
|
var selected = controller.SelectVariant(_sampleVariants);
|
|
|
|
Assert.NotNull(selected);
|
|
Assert.Equal(600_000, selected.Bandwidth);
|
|
Assert.Equal(360, selected.Height);
|
|
}
|
|
|
|
[Fact]
|
|
public void Gate467_AdaptiveStreamingBitrateController_SelectsOptimalVariant_UnderDynamicAdaptiveStrategy()
|
|
{
|
|
var controller = new AdaptiveStreamingBitrateController(BitrateSelectionStrategy.DynamicAdaptive);
|
|
|
|
// Network throughput: 3,500,000 bps. With safety headroom 0.85 -> safe budget = 2,975,000 bps
|
|
// Should select 720p (2,800,000 bps)
|
|
controller.SetEstimatedBandwidth(3_500_000);
|
|
var selected = controller.SelectVariant(_sampleVariants);
|
|
|
|
Assert.NotNull(selected);
|
|
Assert.Equal(2_800_000, selected.Bandwidth);
|
|
Assert.Equal(720, selected.Height);
|
|
}
|
|
|
|
[Fact]
|
|
public void Gate468_AdaptiveStreamingBitrateController_EnforcesTargetCap_WhenConfigured()
|
|
{
|
|
// Strategy is Highest, but MaxCap is 3,000,000 bps -> should cap at 720p (2.8M)
|
|
var controller = new AdaptiveStreamingBitrateController(
|
|
strategy: BitrateSelectionStrategy.HighestQuality,
|
|
maxBitrateCapBps: 3_000_000
|
|
);
|
|
|
|
var selected = controller.SelectVariant(_sampleVariants);
|
|
|
|
Assert.NotNull(selected);
|
|
Assert.Equal(2_800_000, selected.Bandwidth);
|
|
Assert.Equal(720, selected.Height);
|
|
}
|
|
|
|
[Fact]
|
|
public void Gate469_AdaptiveStreamingBitrateController_AppliesHysteresisDamping_ToPreventThrashing()
|
|
{
|
|
var controller = new AdaptiveStreamingBitrateController(
|
|
strategy: BitrateSelectionStrategy.DynamicAdaptive,
|
|
consecutiveSamplesForSwitch: 2
|
|
);
|
|
|
|
// Initial: 1080p
|
|
controller.SetEstimatedBandwidth(10_000_000);
|
|
var initial = controller.SelectVariant(_sampleVariants);
|
|
Assert.Equal(1080, initial.Height);
|
|
|
|
// Transient dip for 1 sample: 1,500,000 bps (480p territory: 1.5M * 0.85 = 1.275M >= 1.2M)
|
|
controller.SetEstimatedBandwidth(1_500_000);
|
|
var damped = controller.SelectVariant(_sampleVariants);
|
|
// Hysteresis holds 1080p on first dip!
|
|
Assert.Equal(1080, damped.Height);
|
|
|
|
// Second consecutive dip confirms downshift
|
|
var confirmed = controller.SelectVariant(_sampleVariants);
|
|
Assert.Equal(480, confirmed.Height);
|
|
}
|
|
|
|
[Fact]
|
|
public void Gate470_AdaptiveStreamingBitrateController_IntegratesWithBatterySaver_ToDownshift()
|
|
{
|
|
var powerManager = new BatterySaverPowerManager();
|
|
// Simulate battery at 15% (Critical battery saving active)
|
|
powerManager.UpdateBatteryState(percent: 15, isCharging: false);
|
|
|
|
var controller = new AdaptiveStreamingBitrateController(
|
|
strategy: BitrateSelectionStrategy.DynamicAdaptive,
|
|
powerManager: powerManager
|
|
);
|
|
|
|
// Even with 10Gbps unlimited network, Battery Saver enforces max 720p / 3Mbps to preserve CPU/GPU decode cycles
|
|
controller.SetEstimatedBandwidth(100_000_000);
|
|
var selected = controller.SelectVariant(_sampleVariants);
|
|
|
|
Assert.NotNull(selected);
|
|
Assert.True(selected.Bandwidth <= 3_000_000);
|
|
Assert.True(selected.Height <= 720);
|
|
}
|
|
}
|