75 lines
4.9 KiB
C#
75 lines
4.9 KiB
C#
using System;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Net;
|
|
using System.Net.Sockets;
|
|
using System.Reflection;
|
|
using System.Text.Json;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using Jellyfin.Plugin.Enigma2;
|
|
using MediaBrowser.Controller.MediaEncoding;
|
|
using Microsoft.AspNetCore.Builder;
|
|
using Microsoft.AspNetCore.Hosting;
|
|
using Microsoft.AspNetCore.Http;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
public static class PatientStartupE2E
|
|
{
|
|
private static int connections, active, maximum;
|
|
public static async Task Run(string encoder, string folder)
|
|
{
|
|
folder=Path.GetFullPath(folder);if(Directory.Exists(folder))throw new Exception("Choose a new output directory");Directory.CreateDirectory(folder);
|
|
var file=Path.Combine(folder,"fixture.ts");
|
|
var info=new ProcessStartInfo(encoder){UseShellExecute=false,RedirectStandardError=true};
|
|
foreach(var a in new[]{"-v","error","-nostdin","-filter_threads","1","-f","lavfi","-i","testsrc2=size=320x180:rate=25","-f","lavfi","-i","sine=sample_rate=48000","-t","4","-c:v","libx264","-threads","1","-preset","ultrafast","-g","25","-c:a","aac","-ac","2","-f","mpegts",file})info.ArgumentList.Add(a);
|
|
using(var make=Process.Start(info)){var error=make.StandardError.ReadToEndAsync();await make.WaitForExitAsync();if(make.ExitCode!=0)throw new Exception(await error);}
|
|
var bytes=await File.ReadAllBytesAsync(file);
|
|
using var listener=new TcpListener(IPAddress.Loopback,0);listener.Start();var port=((IPEndPoint)listener.LocalEndpoint).Port;listener.Stop();
|
|
var builder=WebApplication.CreateBuilder();builder.Logging.ClearProviders();builder.WebHost.UseUrls($"http://127.0.0.1:{port}");
|
|
using var relay=new LiveStreamRelay(new System.Net.Http.HttpClient(),port,TimeSpan.FromMilliseconds(200),TimeSpan.FromSeconds(StartupBudget.Seconds),TimeSpan.FromSeconds(StartupBudget.StallSeconds));
|
|
builder.Services.AddSingleton(relay);builder.Services.AddControllers().AddApplicationPart(typeof(LiveRelayController).Assembly);
|
|
await using var app=builder.Build();app.MapControllers();
|
|
app.MapGet("/late.ts",async context=>
|
|
{
|
|
Interlocked.Increment(ref connections);maximum=Math.Max(maximum,Interlocked.Increment(ref active));
|
|
context.Response.ContentType="video/mp2t";await context.Response.StartAsync(context.RequestAborted);
|
|
try
|
|
{
|
|
await Task.Delay(TimeSpan.FromSeconds(35),context.RequestAborted);
|
|
while(!context.RequestAborted.IsCancellationRequested)
|
|
for(var at=0;at<bytes.Length;at+=188*32)
|
|
{
|
|
var n=Math.Min(188*32,bytes.Length-at);
|
|
await context.Response.Body.WriteAsync(bytes.AsMemory(at,n),context.RequestAborted);await context.Response.Body.FlushAsync(context.RequestAborted);
|
|
await Task.Delay(TimeSpan.FromSeconds(4d*n/bytes.Length),context.RequestAborted);
|
|
}
|
|
}
|
|
catch(OperationCanceledException){}catch(IOException){}
|
|
finally{Interlocked.Decrement(ref active);}
|
|
});
|
|
await app.StartAsync();
|
|
try
|
|
{
|
|
var proxy=DispatchProxy.Create<IMediaEncoder,PatientEncoderProxy>();((PatientEncoderProxy)(object)proxy).ProbePath=Path.Combine(Path.GetDirectoryName(encoder),"ffprobe");
|
|
var probe=new LiveStreamProbe(proxy);var url=relay.Register($"http://127.0.0.1:{port}/late.ts");var watch=Stopwatch.StartNew();
|
|
var result=await Task.WhenAll(Enumerable.Range(0,4).Select(_=>probe.Get(url,CancellationToken.None,1))).WaitAsync(TimeSpan.FromSeconds(95));
|
|
if(watch.Elapsed.TotalSeconds<35||watch.Elapsed.TotalSeconds>=90||result.Any(x=>x[0].Width!=320||x[0].Height!=180))throw new Exception("Delayed source failed the configured budget");
|
|
await Task.Delay(800);
|
|
if(maximum!=1||connections!=1||active!=0)throw new Exception("Receiver sharing or cleanup failed");
|
|
var report=new{Passed=true,SyntheticOnly=true,ReceiverConnectionsOpened=0,ConfiguredStartupSeconds=StartupBudget.Seconds,DelayedFirstDataSeconds=35,
|
|
ElapsedSeconds=watch.Elapsed.TotalSeconds,ConcurrentRequesters=4,PhysicalFixtureConnections=connections,MaximumConcurrentFixtureConnections=maximum,RemainingConnections=active,
|
|
ActualFfprobeUsed=true,PhysicalAndroidTvTested=false};
|
|
await File.WriteAllTextAsync(Path.Combine(folder,"result.json"),JsonSerializer.Serialize(report,new JsonSerializerOptions{WriteIndented=true}));Console.WriteLine(JsonSerializer.Serialize(report));
|
|
}
|
|
finally{relay.Dispose();await app.StopAsync();}
|
|
}
|
|
}
|
|
public class PatientEncoderProxy : DispatchProxy
|
|
{
|
|
public string ProbePath;
|
|
protected override object Invoke(MethodInfo method,object[] args)=>method.Name=="get_ProbePath"?ProbePath:null;
|
|
}
|