mirror of https://github.com/ErsatzTV/ErsatzTV.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
150 lines
5.9 KiB
150 lines
5.9 KiB
using System.IO.Pipelines; |
|
using System.Runtime.InteropServices; |
|
using System.Text.Json; |
|
using System.Text.Json.Serialization; |
|
using CliWrap; |
|
using ErsatzTV.Core; |
|
using ErsatzTV.Core.Domain; |
|
using ErsatzTV.Core.Interfaces.Locking; |
|
using ErsatzTV.Core.Interfaces.Streaming; |
|
using ErsatzTV.Core.Interfaces.Troubleshooting; |
|
using ErsatzTV.Core.Notifications; |
|
using ErsatzTV.FFmpeg.Runtime; |
|
using Microsoft.Extensions.Logging; |
|
|
|
namespace ErsatzTV.Application.Troubleshooting; |
|
|
|
public class StartTroubleshootingPlaybackHandler( |
|
ITroubleshootingNotifier notifier, |
|
IMediator mediator, |
|
IEntityLocker entityLocker, |
|
IRuntimeInfo runtimeInfo, |
|
IGraphicsEngine graphicsEngine, |
|
ILogger<StartTroubleshootingPlaybackHandler> logger) |
|
: IRequestHandler<StartTroubleshootingPlayback> |
|
{ |
|
private static readonly JsonSerializerOptions Options = new() |
|
{ |
|
Converters = { new JsonStringEnumConverter() }, |
|
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, |
|
WriteIndented = true |
|
}; |
|
|
|
public async Task Handle(StartTroubleshootingPlayback request, CancellationToken cancellationToken) |
|
{ |
|
try |
|
{ |
|
// write media info without title |
|
string infoJson = JsonSerializer.Serialize(request.MediaItemInfo with { Title = null }, Options); |
|
await File.WriteAllTextAsync( |
|
Path.Combine(FileSystemLayout.TranscodeTroubleshootingFolder, "media_info.json"), |
|
infoJson, |
|
cancellationToken); |
|
|
|
// write troubleshooting info |
|
string troubleshootingInfoJson = JsonSerializer.Serialize( |
|
new |
|
{ |
|
request.TroubleshootingInfo.Version, |
|
Environment = request.TroubleshootingInfo.Environment.OrderBy(x => x.Key) |
|
.ToDictionary(x => x.Key, x => x.Value), |
|
request.TroubleshootingInfo.Health, |
|
request.TroubleshootingInfo.Cpus, |
|
request.TroubleshootingInfo.VideoControllers, |
|
request.TroubleshootingInfo.FFmpegSettings, |
|
request.TroubleshootingInfo.FFmpegProfiles, |
|
request.TroubleshootingInfo.Watermarks |
|
}, |
|
Options); |
|
await File.WriteAllTextAsync( |
|
Path.Combine(FileSystemLayout.TranscodeTroubleshootingFolder, "troubleshooting_info.json"), |
|
troubleshootingInfoJson, |
|
cancellationToken); |
|
|
|
HardwareAccelerationKind hwAccel = request.TroubleshootingInfo.FFmpegProfiles.Head().HardwareAcceleration; |
|
if (hwAccel is HardwareAccelerationKind.Qsv) |
|
{ |
|
await File.WriteAllTextAsync( |
|
Path.Combine(FileSystemLayout.TranscodeTroubleshootingFolder, "capabilities_qsv.txt"), |
|
request.TroubleshootingInfo.QsvCapabilities, |
|
cancellationToken); |
|
} |
|
|
|
if (hwAccel is HardwareAccelerationKind.Vaapi || (hwAccel is HardwareAccelerationKind.Qsv && |
|
runtimeInfo.IsOSPlatform(OSPlatform.Linux))) |
|
{ |
|
await File.WriteAllTextAsync( |
|
Path.Combine(FileSystemLayout.TranscodeTroubleshootingFolder, "capabilities_vaapi.txt"), |
|
request.TroubleshootingInfo.VaapiCapabilities, |
|
cancellationToken); |
|
} |
|
|
|
if (hwAccel is HardwareAccelerationKind.Nvenc) |
|
{ |
|
await File.WriteAllTextAsync( |
|
Path.Combine(FileSystemLayout.TranscodeTroubleshootingFolder, "capabilities_nvidia.txt"), |
|
request.TroubleshootingInfo.NvidiaCapabilities, |
|
cancellationToken); |
|
} |
|
|
|
logger.LogDebug("ffmpeg troubleshooting arguments {FFmpegArguments}", request.PlayoutItemResult.Process.Arguments); |
|
|
|
var maybePipe = Option<Pipe>.None; |
|
|
|
try |
|
{ |
|
using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); |
|
|
|
var processWithPipe = request.PlayoutItemResult.Process; |
|
foreach (var graphicsEngineContext in request.PlayoutItemResult.GraphicsEngineContext) |
|
{ |
|
var pipe = new Pipe(); |
|
maybePipe = pipe; |
|
processWithPipe = processWithPipe.WithStandardInputPipe(PipeSource.FromStream(pipe.Reader.AsStream())); |
|
|
|
// fire and forget graphics engine task |
|
_ = graphicsEngine.Run( |
|
graphicsEngineContext, |
|
pipe.Writer, |
|
linkedCts.Token); |
|
} |
|
|
|
CommandResult commandResult = await processWithPipe |
|
.WithStandardErrorPipe(PipeTarget.Null) |
|
.WithValidation(CommandResultValidation.None) |
|
.ExecuteAsync(linkedCts.Token); |
|
|
|
await mediator.Publish( |
|
new PlaybackTroubleshootingCompletedNotification(commandResult.ExitCode), |
|
linkedCts.Token); |
|
|
|
logger.LogDebug("Troubleshooting playback completed with exit code {ExitCode}", commandResult.ExitCode); |
|
|
|
if (commandResult.ExitCode != 0) |
|
{ |
|
await linkedCts.CancelAsync(); |
|
notifier.NotifyFailed(request.SessionId); |
|
} |
|
} |
|
catch (TaskCanceledException) |
|
{ |
|
// do nothing |
|
} |
|
catch (Exception e) |
|
{ |
|
Console.WriteLine(e); |
|
} |
|
finally |
|
{ |
|
foreach (var pipe in maybePipe) |
|
{ |
|
await pipe.Writer.CompleteAsync(); |
|
} |
|
} |
|
} |
|
finally |
|
{ |
|
entityLocker.UnlockTroubleshootingPlayback(); |
|
} |
|
} |
|
}
|
|
|