* load fonts on demand * add new table * populate new table * edit and use multiple watermarks in deco * remove old field * update changelog
151 lines
5.9 KiB
C#
151 lines
5.9 KiB
C#
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();
|
|
}
|
|
}
|
|
}
|