Browse Source

add vaapi display option to ffmpeg profile (#1948)

* add vaapi display option

* fix vaapi capabilities cache key

* update logging
pull/1951/head
Jason Dove 7 months ago committed by GitHub
parent
commit
759052c725
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 4
      CHANGELOG.md
  2. 1
      ErsatzTV.Application/FFmpegProfiles/Commands/CreateFFmpegProfile.cs
  3. 1
      ErsatzTV.Application/FFmpegProfiles/Commands/UpdateFFmpegProfile.cs
  4. 1
      ErsatzTV.Application/FFmpegProfiles/Commands/UpdateFFmpegProfileHandler.cs
  5. 1
      ErsatzTV.Application/FFmpegProfiles/FFmpegProfileViewModel.cs
  6. 2
      ErsatzTV.Application/FFmpegProfiles/Mapper.cs
  7. 1
      ErsatzTV.Application/Streaming/Queries/GetErrorProcessHandler.cs
  8. 4
      ErsatzTV.Application/Streaming/Queries/GetPlayoutItemProcessByChannelNumberHandler.cs
  9. 27
      ErsatzTV.Application/Troubleshooting/Queries/GetTroubleshootingInfoHandler.cs
  10. 1
      ErsatzTV.Core/Api/FFmpegProfiles/FFmpegFullProfileResponseModel.cs
  11. 1
      ErsatzTV.Core/Domain/FFmpegProfile.cs
  12. 12
      ErsatzTV.Core/FFmpeg/FFmpegLibraryProcessService.cs
  13. 2
      ErsatzTV.Core/Interfaces/FFmpeg/IFFmpegProcessService.cs
  14. 54
      ErsatzTV.FFmpeg/Capabilities/HardwareCapabilitiesFactory.cs
  15. 5
      ErsatzTV.FFmpeg/Capabilities/IHardwareCapabilitiesFactory.cs
  16. 1
      ErsatzTV.FFmpeg/Pipeline/IPipelineBuilderFactory.cs
  17. 2
      ErsatzTV.FFmpeg/Pipeline/PipelineBuilderFactory.cs
  18. 5875
      ErsatzTV.Infrastructure.MySql/Migrations/20241125183000_Add_FFmpegProfile_VaapiDisplay.Designer.cs
  19. 29
      ErsatzTV.Infrastructure.MySql/Migrations/20241125183000_Add_FFmpegProfile_VaapiDisplay.cs
  20. 3
      ErsatzTV.Infrastructure.MySql/Migrations/TvContextModelSnapshot.cs
  21. 5714
      ErsatzTV.Infrastructure.Sqlite/Migrations/20241125182911_Add_FFmpegProfile_VaapiDisplay.Designer.cs
  22. 28
      ErsatzTV.Infrastructure.Sqlite/Migrations/20241125182911_Add_FFmpegProfile_VaapiDisplay.cs
  23. 3
      ErsatzTV.Infrastructure.Sqlite/Migrations/TvContextModelSnapshot.cs
  24. 2
      ErsatzTV.Scanner.Tests/Core/FFmpeg/TranscodingTests.cs
  25. 24
      ErsatzTV/Pages/FFmpegEditor.razor
  26. 33
      ErsatzTV/Services/RunOnce/PlatformSettingsService.cs
  27. 4
      ErsatzTV/ViewModels/FFmpegProfileEditViewModel.cs

4
CHANGELOG.md

@ -15,6 +15,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). @@ -15,6 +15,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- `Default` - existing behavior
- `With Progress` - show animated progress bar at bottom of generated video
- Add fallback album art image for songs that have no album art
- Add `Vaapi Display` option to FFmpeg Profile
- Possible values will be install-specific and sourced from `vainfo`
- `drm` was the previous default value, and should be used in most cases
- Test all `Vaapi Display` values in `Troubleshooting` > `VAAPI Capabilities`
### Changed
- **BREAKING CHANGE**: Change channel identifiers used in XMLTV to work around bad behavior in Plex

1
ErsatzTV.Application/FFmpegProfiles/Commands/CreateFFmpegProfile.cs

@ -8,6 +8,7 @@ public record CreateFFmpegProfile( @@ -8,6 +8,7 @@ public record CreateFFmpegProfile(
string Name,
int ThreadCount,
HardwareAccelerationKind HardwareAcceleration,
string VaapiDisplay,
VaapiDriver VaapiDriver,
string VaapiDevice,
int? QsvExtraHardwareFrames,

1
ErsatzTV.Application/FFmpegProfiles/Commands/UpdateFFmpegProfile.cs

@ -9,6 +9,7 @@ public record UpdateFFmpegProfile( @@ -9,6 +9,7 @@ public record UpdateFFmpegProfile(
string Name,
int ThreadCount,
HardwareAccelerationKind HardwareAcceleration,
string VaapiDisplay,
VaapiDriver VaapiDriver,
string VaapiDevice,
int? QsvExtraHardwareFrames,

1
ErsatzTV.Application/FFmpegProfiles/Commands/UpdateFFmpegProfileHandler.cs

@ -36,6 +36,7 @@ public class @@ -36,6 +36,7 @@ public class
p.Name = update.Name;
p.ThreadCount = update.ThreadCount;
p.HardwareAcceleration = update.HardwareAcceleration;
p.VaapiDisplay = update.VaapiDisplay;
p.VaapiDriver = update.VaapiDriver;
p.VaapiDevice = update.VaapiDevice;
p.QsvExtraHardwareFrames = update.QsvExtraHardwareFrames;

1
ErsatzTV.Application/FFmpegProfiles/FFmpegProfileViewModel.cs

@ -9,6 +9,7 @@ public record FFmpegProfileViewModel( @@ -9,6 +9,7 @@ public record FFmpegProfileViewModel(
string Name,
int ThreadCount,
HardwareAccelerationKind HardwareAcceleration,
string VaapiDisplay,
VaapiDriver VaapiDriver,
string VaapiDevice,
int? QsvExtraHardwareFrames,

2
ErsatzTV.Application/FFmpegProfiles/Mapper.cs

@ -11,6 +11,7 @@ internal static class Mapper @@ -11,6 +11,7 @@ internal static class Mapper
profile.Name,
profile.ThreadCount,
profile.HardwareAcceleration,
profile.VaapiDisplay ?? "drm",
profile.VaapiDriver,
profile.VaapiDevice,
profile.QsvExtraHardwareFrames,
@ -46,6 +47,7 @@ internal static class Mapper @@ -46,6 +47,7 @@ internal static class Mapper
ffmpegProfile.Name,
ffmpegProfile.ThreadCount,
(int)ffmpegProfile.HardwareAcceleration,
ffmpegProfile.VaapiDisplay,
(int)ffmpegProfile.VaapiDriver,
ffmpegProfile.VaapiDevice,
ffmpegProfile.ResolutionId,

1
ErsatzTV.Application/Streaming/Queries/GetErrorProcessHandler.cs

@ -32,6 +32,7 @@ public class GetErrorProcessHandler : FFmpegProcessHandler<GetErrorProcess> @@ -32,6 +32,7 @@ public class GetErrorProcessHandler : FFmpegProcessHandler<GetErrorProcess>
request.ErrorMessage,
request.HlsRealtime,
request.PtsOffset,
channel.FFmpegProfile.VaapiDisplay,
channel.FFmpegProfile.VaapiDriver,
channel.FFmpegProfile.VaapiDevice,
Optional(channel.FFmpegProfile.QsvExtraHardwareFrames));

4
ErsatzTV.Application/Streaming/Queries/GetPlayoutItemProcessByChannelNumberHandler.cs

@ -312,6 +312,7 @@ public class GetPlayoutItemProcessByChannelNumberHandler : FFmpegProcessHandler< @@ -312,6 +312,7 @@ public class GetPlayoutItemProcessByChannelNumberHandler : FFmpegProcessHandler<
request.StartAtZero ? start : now,
playoutItemWatermark,
maybeGlobalWatermark,
channel.FFmpegProfile.VaapiDisplay,
channel.FFmpegProfile.VaapiDriver,
channel.FFmpegProfile.VaapiDevice,
Optional(channel.FFmpegProfile.QsvExtraHardwareFrames),
@ -357,6 +358,7 @@ public class GetPlayoutItemProcessByChannelNumberHandler : FFmpegProcessHandler< @@ -357,6 +358,7 @@ public class GetPlayoutItemProcessByChannelNumberHandler : FFmpegProcessHandler<
"Channel is Offline",
request.HlsRealtime,
request.PtsOffset,
channel.FFmpegProfile.VaapiDisplay,
channel.FFmpegProfile.VaapiDriver,
channel.FFmpegProfile.VaapiDevice,
Optional(channel.FFmpegProfile.QsvExtraHardwareFrames));
@ -370,6 +372,7 @@ public class GetPlayoutItemProcessByChannelNumberHandler : FFmpegProcessHandler< @@ -370,6 +372,7 @@ public class GetPlayoutItemProcessByChannelNumberHandler : FFmpegProcessHandler<
error.Value,
request.HlsRealtime,
request.PtsOffset,
channel.FFmpegProfile.VaapiDisplay,
channel.FFmpegProfile.VaapiDriver,
channel.FFmpegProfile.VaapiDevice,
Optional(channel.FFmpegProfile.QsvExtraHardwareFrames));
@ -383,6 +386,7 @@ public class GetPlayoutItemProcessByChannelNumberHandler : FFmpegProcessHandler< @@ -383,6 +386,7 @@ public class GetPlayoutItemProcessByChannelNumberHandler : FFmpegProcessHandler<
"Channel is Offline",
request.HlsRealtime,
request.PtsOffset,
channel.FFmpegProfile.VaapiDisplay,
channel.FFmpegProfile.VaapiDriver,
channel.FFmpegProfile.VaapiDevice,
Optional(channel.FFmpegProfile.QsvExtraHardwareFrames));

27
ErsatzTV.Application/Troubleshooting/Queries/GetTroubleshootingInfoHandler.cs

@ -92,7 +92,12 @@ public class GetTroubleshootingInfoHandler : IRequestHandler<GetTroubleshootingI @@ -92,7 +92,12 @@ public class GetTroubleshootingInfoHandler : IRequestHandler<GetTroubleshootingI
if (!_memoryCache.TryGetValue("ffmpeg.render_devices", out List<string> vaapiDevices))
{
vaapiDevices = new List<string> { "/dev/dri/renderD128" };
vaapiDevices = ["/dev/dri/renderD128"];
}
if (!_memoryCache.TryGetValue("ffmpeg.vaapi_displays", out List<string> vaapiDisplays))
{
vaapiDisplays = ["drm"];
}
foreach (string qsvDevice in vaapiDevices)
@ -109,19 +114,19 @@ public class GetTroubleshootingInfoHandler : IRequestHandler<GetTroubleshootingI @@ -109,19 +114,19 @@ public class GetTroubleshootingInfoHandler : IRequestHandler<GetTroubleshootingI
var allDrivers = new List<VaapiDriver>
{ VaapiDriver.iHD, VaapiDriver.i965, VaapiDriver.RadeonSI, VaapiDriver.Nouveau };
foreach (string display in vaapiDisplays)
foreach (VaapiDriver activeDriver in allDrivers)
foreach (string vaapiDevice in vaapiDevices)
{
foreach (string vaapiDevice in vaapiDevices)
foreach (string output in await _hardwareCapabilitiesFactory.GetVaapiOutput(
display,
Optional(GetDriverName(activeDriver)),
vaapiDevice))
{
foreach (string output in await _hardwareCapabilitiesFactory.GetVaapiOutput(
Optional(GetDriverName(activeDriver)),
vaapiDevice))
{
vaapiCapabilities +=
$"Checking driver {activeDriver} device {vaapiDevice}{Environment.NewLine}{Environment.NewLine}";
vaapiCapabilities += output;
vaapiCapabilities += Environment.NewLine + Environment.NewLine;
}
vaapiCapabilities +=
$"Checking display [{display}] driver [{activeDriver}] device [{vaapiDevice}]{Environment.NewLine}{Environment.NewLine}";
vaapiCapabilities += output;
vaapiCapabilities += Environment.NewLine + Environment.NewLine;
}
}
}

1
ErsatzTV.Core/Api/FFmpegProfiles/FFmpegFullProfileResponseModel.cs

@ -5,6 +5,7 @@ public record FFmpegFullProfileResponseModel( @@ -5,6 +5,7 @@ public record FFmpegFullProfileResponseModel(
string Name,
int ThreadCount,
int HardwareAcceleration,
string VaapiDisplay,
int VaapiDriver,
string VaapiDevice,
int ResolutionId,

1
ErsatzTV.Core/Domain/FFmpegProfile.cs

@ -8,6 +8,7 @@ public record FFmpegProfile @@ -8,6 +8,7 @@ public record FFmpegProfile
public string Name { get; set; }
public int ThreadCount { get; set; }
public HardwareAccelerationKind HardwareAcceleration { get; set; }
public string VaapiDisplay { get; set; }
public VaapiDriver VaapiDriver { get; set; }
public string VaapiDevice { get; set; }
public int? QsvExtraHardwareFrames { get; set; }

12
ErsatzTV.Core/FFmpeg/FFmpegLibraryProcessService.cs

@ -59,6 +59,7 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService @@ -59,6 +59,7 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService
DateTimeOffset now,
Option<ChannelWatermark> playoutItemWatermark,
Option<ChannelWatermark> globalWatermark,
string vaapiDisplay,
VaapiDriver vaapiDriver,
string vaapiDevice,
Option<int> qsvExtraHardwareFrames,
@ -408,6 +409,7 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService @@ -408,6 +409,7 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService
watermarkInputFile,
subtitleInputFile,
Option<ConcatInputFile>.None,
VaapiDisplayName(hwAccel, vaapiDisplay),
VaapiDriverName(hwAccel, vaapiDriver),
VaapiDeviceName(hwAccel, vaapiDevice),
FileSystemLayout.FFmpegReportsFolder,
@ -428,6 +430,7 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService @@ -428,6 +430,7 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService
string errorMessage,
bool hlsRealtime,
long ptsOffset,
string vaapiDisplay,
VaapiDriver vaapiDriver,
string vaapiDevice,
Option<int> qsvExtraHardwareFrames)
@ -569,6 +572,7 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService @@ -569,6 +572,7 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService
None,
subtitleInputFile,
Option<ConcatInputFile>.None,
VaapiDisplayName(hwAccel, vaapiDisplay),
VaapiDriverName(hwAccel, vaapiDriver),
VaapiDeviceName(hwAccel, vaapiDevice),
FileSystemLayout.FFmpegReportsFolder,
@ -600,6 +604,7 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService @@ -600,6 +604,7 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService
None,
None,
concatInputFile,
Option<string>.None,
None,
None,
FileSystemLayout.FFmpegReportsFolder,
@ -717,6 +722,7 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService @@ -717,6 +722,7 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService
playbackSettings.VideoTrackTimeScale,
playbackSettings.Deinterlace);
Option<string> vaapiDisplay = VaapiDisplayName(hwAccel, channel.FFmpegProfile.VaapiDisplay);
Option<string> vaapiDriver = VaapiDriverName(hwAccel, channel.FFmpegProfile.VaapiDriver);
Option<string> vaapiDevice = VaapiDeviceName(hwAccel, channel.FFmpegProfile.VaapiDevice);
@ -751,6 +757,7 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService @@ -751,6 +757,7 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService
watermarkInputFile,
subtitleInputFile,
concatInputFile,
vaapiDisplay,
vaapiDriver,
vaapiDevice,
FileSystemLayout.FFmpegReportsFolder,
@ -790,6 +797,7 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService @@ -790,6 +797,7 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService
None,
None,
concatInputFile,
Option<string>.None,
None,
None,
FileSystemLayout.FFmpegReportsFolder,
@ -830,6 +838,7 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService @@ -830,6 +838,7 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService
None,
None,
Option<ConcatInputFile>.None,
Option<string>.None,
None,
None,
FileSystemLayout.FFmpegReportsFolder,
@ -988,6 +997,9 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService @@ -988,6 +997,9 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService
.WithEnvironmentVariables(environmentVariables.ToDictionary(e => e.Key, e => e.Value));
}
private static Option<string> VaapiDisplayName(HardwareAccelerationMode accelerationMode, string vaapiDisplay) =>
accelerationMode == HardwareAccelerationMode.Vaapi ? vaapiDisplay : Option<string>.None;
private static Option<string> VaapiDriverName(HardwareAccelerationMode accelerationMode, VaapiDriver driver)
{
if (accelerationMode == HardwareAccelerationMode.Vaapi)

2
ErsatzTV.Core/Interfaces/FFmpeg/IFFmpegProcessService.cs

@ -28,6 +28,7 @@ public interface IFFmpegProcessService @@ -28,6 +28,7 @@ public interface IFFmpegProcessService
DateTimeOffset now,
Option<ChannelWatermark> playoutItemWatermark,
Option<ChannelWatermark> globalWatermark,
string vaapiDisplay,
VaapiDriver vaapiDriver,
string vaapiDevice,
Option<int> qsvExtraHardwareFrames,
@ -47,6 +48,7 @@ public interface IFFmpegProcessService @@ -47,6 +48,7 @@ public interface IFFmpegProcessService
string errorMessage,
bool hlsRealtime,
long ptsOffset,
string vaapiDisplay,
VaapiDriver vaapiDriver,
string vaapiDevice,
Option<int> qsvExtraHardwareFrames);

54
ErsatzTV.FFmpeg/Capabilities/HardwareCapabilitiesFactory.cs

@ -20,7 +20,7 @@ public class HardwareCapabilitiesFactory : IHardwareCapabilitiesFactory @@ -20,7 +20,7 @@ public class HardwareCapabilitiesFactory : IHardwareCapabilitiesFactory
private const string ModelCacheKey = "ffmpeg.hardware.nvidia.model";
private static readonly CompositeFormat
VaapiCacheKeyFormat = CompositeFormat.Parse("ffmpeg.hardware.vaapi.{0}.{1}");
VaapiCacheKeyFormat = CompositeFormat.Parse("ffmpeg.hardware.vaapi.{0}.{1}.{2}");
private static readonly CompositeFormat QsvCacheKeyFormat = CompositeFormat.Parse("ffmpeg.hardware.qsv.{0}");
private static readonly CompositeFormat FFmpegCapabilitiesCacheKeyFormat = CompositeFormat.Parse("ffmpeg.{0}");
@ -82,6 +82,7 @@ public class HardwareCapabilitiesFactory : IHardwareCapabilitiesFactory @@ -82,6 +82,7 @@ public class HardwareCapabilitiesFactory : IHardwareCapabilitiesFactory
IFFmpegCapabilities ffmpegCapabilities,
string ffmpegPath,
HardwareAccelerationMode hardwareAccelerationMode,
Option<string> vaapiDisplay,
Option<string> vaapiDriver,
Option<string> vaapiDevice)
{
@ -103,7 +104,7 @@ public class HardwareCapabilitiesFactory : IHardwareCapabilitiesFactory @@ -103,7 +104,7 @@ public class HardwareCapabilitiesFactory : IHardwareCapabilitiesFactory
{
HardwareAccelerationMode.Nvenc => await GetNvidiaCapabilities(ffmpegPath, ffmpegCapabilities),
HardwareAccelerationMode.Qsv => await GetQsvCapabilities(ffmpegPath, vaapiDevice),
HardwareAccelerationMode.Vaapi => await GetVaapiCapabilities(vaapiDriver, vaapiDevice),
HardwareAccelerationMode.Vaapi => await GetVaapiCapabilities(vaapiDisplay, vaapiDriver, vaapiDevice),
HardwareAccelerationMode.Amf => new AmfHardwareCapabilities(),
_ => new DefaultHardwareCapabilities()
};
@ -151,7 +152,7 @@ public class HardwareCapabilitiesFactory : IHardwareCapabilitiesFactory @@ -151,7 +152,7 @@ public class HardwareCapabilitiesFactory : IHardwareCapabilitiesFactory
return new QsvOutput(result.ExitCode, output);
}
public async Task<Option<string>> GetVaapiOutput(Option<string> vaapiDriver, string vaapiDevice)
public async Task<Option<string>> GetVaapiOutput(string display, Option<string> vaapiDriver, string vaapiDevice)
{
BufferedCommandResult whichResult = await Cli.Wrap("which")
.WithArguments("vainfo")
@ -169,13 +170,43 @@ public class HardwareCapabilitiesFactory : IHardwareCapabilitiesFactory @@ -169,13 +170,43 @@ public class HardwareCapabilitiesFactory : IHardwareCapabilitiesFactory
envVars.Add("LIBVA_DRIVER_NAME", libvaDriverName);
}
BufferedCommandResult result = await Cli.Wrap("vainfo")
.WithArguments($"--display drm --device {vaapiDevice} -a")
var lines = new List<string>();
await Cli.Wrap("vainfo")
.WithArguments($"--display {display} --device {vaapiDevice} -a")
.WithEnvironmentVariables(envVars)
.WithValidation(CommandResultValidation.None)
.WithStandardOutputPipe(PipeTarget.ToDelegate(lines.Add))
.WithStandardErrorPipe(PipeTarget.ToDelegate(lines.Add))
.ExecuteAsync();
var mergedOutput = string.Join(System.Environment.NewLine, lines);
return mergedOutput.Contains("trying display", StringComparison.OrdinalIgnoreCase)
? mergedOutput
: string.Empty;
}
public async Task<List<string>> GetVaapiDisplays()
{
BufferedCommandResult whichResult = await Cli.Wrap("which")
.WithArguments("vainfo")
.WithValidation(CommandResultValidation.None)
.ExecuteBufferedAsync(Encoding.UTF8);
if (whichResult.ExitCode != 0)
{
return ["drm"];
}
BufferedCommandResult result = await Cli.Wrap("vainfo")
.WithArguments("--display help")
.WithValidation(CommandResultValidation.None)
.ExecuteBufferedAsync(Encoding.UTF8);
return result.StandardOutput;
return string.IsNullOrWhiteSpace(result.StandardOutput)
? ["drm"]
: result.StandardOutput.Trim().Split("\n").Skip(1).Map(s => s.Trim()).ToList();
}
private async Task<IReadOnlySet<string>> GetFFmpegCapabilities(
@ -253,6 +284,7 @@ public class HardwareCapabilitiesFactory : IHardwareCapabilitiesFactory @@ -253,6 +284,7 @@ public class HardwareCapabilitiesFactory : IHardwareCapabilitiesFactory
}
private async Task<IHardwareCapabilities> GetVaapiCapabilities(
Option<string> vaapiDisplay,
Option<string> vaapiDriver,
Option<string> vaapiDevice)
{
@ -269,9 +301,10 @@ public class HardwareCapabilitiesFactory : IHardwareCapabilitiesFactory @@ -269,9 +301,10 @@ public class HardwareCapabilitiesFactory : IHardwareCapabilitiesFactory
return new NoHardwareCapabilities();
}
string display = vaapiDisplay.IfNone("drm");
string driver = vaapiDriver.IfNone(string.Empty);
string device = vaapiDevice.IfNone(string.Empty);
var cacheKey = string.Format(CultureInfo.InvariantCulture, VaapiCacheKeyFormat, driver, device);
var cacheKey = string.Format(CultureInfo.InvariantCulture, VaapiCacheKeyFormat, display, driver, device);
if (_memoryCache.TryGetValue(cacheKey, out List<VaapiProfileEntrypoint>? profileEntrypoints) &&
profileEntrypoints is not null)
@ -279,7 +312,7 @@ public class HardwareCapabilitiesFactory : IHardwareCapabilitiesFactory @@ -279,7 +312,7 @@ public class HardwareCapabilitiesFactory : IHardwareCapabilitiesFactory
return new VaapiHardwareCapabilities(profileEntrypoints, _logger);
}
Option<string> output = await GetVaapiOutput(vaapiDriver, device);
Option<string> output = await GetVaapiOutput(display, vaapiDriver, device);
if (output.IsNone)
{
_logger.LogWarning("Unable to determine VAAPI capabilities; please install vainfo");
@ -294,8 +327,9 @@ public class HardwareCapabilitiesFactory : IHardwareCapabilitiesFactory @@ -294,8 +327,9 @@ public class HardwareCapabilitiesFactory : IHardwareCapabilitiesFactory
if (profileEntrypoints is not null && profileEntrypoints.Count != 0)
{
_logger.LogDebug(
"Detected {Count} VAAPI profile entrypoints for using {Driver} {Device}",
"Detected {Count} VAAPI profile entrypoints for using {Display} {Driver} {Device}",
profileEntrypoints.Count,
display,
driver,
device);
_memoryCache.Set(cacheKey, profileEntrypoints);
@ -345,7 +379,7 @@ public class HardwareCapabilitiesFactory : IHardwareCapabilitiesFactory @@ -345,7 +379,7 @@ public class HardwareCapabilitiesFactory : IHardwareCapabilitiesFactory
if (_runtimeInfo.IsOSPlatform(OSPlatform.Linux))
{
Option<string> vaapiOutput = await GetVaapiOutput(Option<string>.None, device);
Option<string> vaapiOutput = await GetVaapiOutput("drm", Option<string>.None, device);
if (vaapiOutput.IsNone)
{
_logger.LogWarning("Unable to determine QSV capabilities; please install vainfo");

5
ErsatzTV.FFmpeg/Capabilities/IHardwareCapabilitiesFactory.cs

@ -10,6 +10,7 @@ public interface IHardwareCapabilitiesFactory @@ -10,6 +10,7 @@ public interface IHardwareCapabilitiesFactory
IFFmpegCapabilities ffmpegCapabilities,
string ffmpegPath,
HardwareAccelerationMode hardwareAccelerationMode,
Option<string> vaapiDisplay,
Option<string> vaapiDriver,
Option<string> vaapiDevice);
@ -17,5 +18,7 @@ public interface IHardwareCapabilitiesFactory @@ -17,5 +18,7 @@ public interface IHardwareCapabilitiesFactory
Task<QsvOutput> GetQsvOutput(string ffmpegPath, Option<string> qsvDevice);
Task<Option<string>> GetVaapiOutput(Option<string> vaapiDriver, string vaapiDevice);
Task<Option<string>> GetVaapiOutput(string display, Option<string> vaapiDriver, string vaapiDevice);
Task<List<string>> GetVaapiDisplays();
}

1
ErsatzTV.FFmpeg/Pipeline/IPipelineBuilderFactory.cs

@ -9,6 +9,7 @@ public interface IPipelineBuilderFactory @@ -9,6 +9,7 @@ public interface IPipelineBuilderFactory
Option<WatermarkInputFile> watermarkInputFile,
Option<SubtitleInputFile> subtitleInputFile,
Option<ConcatInputFile> concatInputFile,
Option<string> vaapiDisplay,
Option<string> vaapiDriver,
Option<string> vaapiDevice,
string reportsFolder,

2
ErsatzTV.FFmpeg/Pipeline/PipelineBuilderFactory.cs

@ -23,6 +23,7 @@ public class PipelineBuilderFactory : IPipelineBuilderFactory @@ -23,6 +23,7 @@ public class PipelineBuilderFactory : IPipelineBuilderFactory
Option<WatermarkInputFile> watermarkInputFile,
Option<SubtitleInputFile> subtitleInputFile,
Option<ConcatInputFile> concatInputFile,
Option<string> vaapiDisplay,
Option<string> vaapiDriver,
Option<string> vaapiDevice,
string reportsFolder,
@ -35,6 +36,7 @@ public class PipelineBuilderFactory : IPipelineBuilderFactory @@ -35,6 +36,7 @@ public class PipelineBuilderFactory : IPipelineBuilderFactory
ffmpegCapabilities,
ffmpegPath,
hardwareAccelerationMode,
vaapiDisplay,
vaapiDriver,
vaapiDevice);

5875
ErsatzTV.Infrastructure.MySql/Migrations/20241125183000_Add_FFmpegProfile_VaapiDisplay.Designer.cs generated

File diff suppressed because it is too large Load Diff

29
ErsatzTV.Infrastructure.MySql/Migrations/20241125183000_Add_FFmpegProfile_VaapiDisplay.cs

@ -0,0 +1,29 @@ @@ -0,0 +1,29 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace ErsatzTV.Infrastructure.MySql.Migrations
{
/// <inheritdoc />
public partial class Add_FFmpegProfile_VaapiDisplay : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "VaapiDisplay",
table: "FFmpegProfile",
type: "longtext",
nullable: true)
.Annotation("MySql:CharSet", "utf8mb4");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "VaapiDisplay",
table: "FFmpegProfile");
}
}
}

3
ErsatzTV.Infrastructure.MySql/Migrations/TvContextModelSnapshot.cs

@ -640,6 +640,9 @@ namespace ErsatzTV.Infrastructure.MySql.Migrations @@ -640,6 +640,9 @@ namespace ErsatzTV.Infrastructure.MySql.Migrations
b.Property<string>("VaapiDevice")
.HasColumnType("longtext");
b.Property<string>("VaapiDisplay")
.HasColumnType("longtext");
b.Property<int>("VaapiDriver")
.HasColumnType("int");

5714
ErsatzTV.Infrastructure.Sqlite/Migrations/20241125182911_Add_FFmpegProfile_VaapiDisplay.Designer.cs generated

File diff suppressed because it is too large Load Diff

28
ErsatzTV.Infrastructure.Sqlite/Migrations/20241125182911_Add_FFmpegProfile_VaapiDisplay.cs

@ -0,0 +1,28 @@ @@ -0,0 +1,28 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace ErsatzTV.Infrastructure.Sqlite.Migrations
{
/// <inheritdoc />
public partial class Add_FFmpegProfile_VaapiDisplay : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "VaapiDisplay",
table: "FFmpegProfile",
type: "TEXT",
nullable: true);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "VaapiDisplay",
table: "FFmpegProfile");
}
}
}

3
ErsatzTV.Infrastructure.Sqlite/Migrations/TvContextModelSnapshot.cs

@ -609,6 +609,9 @@ namespace ErsatzTV.Infrastructure.Sqlite.Migrations @@ -609,6 +609,9 @@ namespace ErsatzTV.Infrastructure.Sqlite.Migrations
b.Property<string>("VaapiDevice")
.HasColumnType("TEXT");
b.Property<string>("VaapiDisplay")
.HasColumnType("TEXT");
b.Property<int>("VaapiDriver")
.HasColumnType("INTEGER");

2
ErsatzTV.Scanner.Tests/Core/FFmpeg/TranscodingTests.cs

@ -362,6 +362,7 @@ public class TranscodingTests @@ -362,6 +362,7 @@ public class TranscodingTests
now,
Option<ChannelWatermark>.None,
GetWatermark(watermark),
"drm",
VaapiDriver.RadeonSI,
"/dev/dri/renderD128",
Option<int>.None,
@ -639,6 +640,7 @@ public class TranscodingTests @@ -639,6 +640,7 @@ public class TranscodingTests
now,
Option<ChannelWatermark>.None,
channelWatermark,
"drm",
VaapiDriver.RadeonSI,
"/dev/dri/renderD128",
Option<int>.None,

24
ErsatzTV/Pages/FFmpegEditor.razor

@ -107,6 +107,20 @@ @@ -107,6 +107,20 @@
</MudElement>
@if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
@if (_model.HardwareAcceleration == HardwareAccelerationKind.Vaapi)
{
<MudElement HtmlTag="div" Class="mt-3">
<MudSelect Disabled="@(_model.HardwareAcceleration != HardwareAccelerationKind.Vaapi)"
Label="VAAPI Display"
@bind-Value="_model.VaapiDisplay" For="@(() => _model.VaapiDisplay)">
@foreach (string display in _vaapiDisplays)
{
<MudSelectItem Value="@display">@display</MudSelectItem>
}
</MudSelect>
</MudElement>
}
@if (_model.HardwareAcceleration == HardwareAccelerationKind.Vaapi)
{
<MudElement HtmlTag="div" Class="mt-3">
@ -198,7 +212,8 @@ @@ -198,7 +212,8 @@
private List<ResolutionViewModel> _resolutions = new();
private List<HardwareAccelerationKind> _hardwareAccelerationKinds = new();
private List<string> _vaapiDevices = new();
private List<string> _vaapiDisplays = [];
private List<string> _vaapiDevices = [];
private PersistingComponentStateSubscription _persistingSubscription;
public void Dispose()
@ -275,6 +290,13 @@ @@ -275,6 +290,13 @@
}
_vaapiDevices = vaapiDevices.OrderBy(s => s).ToList();
if (!MemoryCache.TryGetValue("ffmpeg.vaapi_displays", out List<string> vaapiDisplays))
{
vaapiDisplays = ["drm"];
}
_vaapiDisplays = vaapiDisplays.OrderBy(s => s).ToList();
}
private Task PersistData()

33
ErsatzTV/Services/RunOnce/PlatformSettingsService.cs

@ -1,5 +1,6 @@ @@ -1,5 +1,6 @@
using System.Runtime.InteropServices;
using ErsatzTV.Core.Interfaces.Metadata;
using ErsatzTV.FFmpeg.Capabilities;
using ErsatzTV.FFmpeg.Runtime;
using Microsoft.Extensions.Caching.Memory;
@ -13,19 +14,31 @@ public class PlatformSettingsService(IServiceScopeFactory serviceScopeFactory) : @@ -13,19 +14,31 @@ public class PlatformSettingsService(IServiceScopeFactory serviceScopeFactory) :
using IServiceScope scope = serviceScopeFactory.CreateScope();
IRuntimeInfo runtimeInfo = scope.ServiceProvider.GetRequiredService<IRuntimeInfo>();
if (runtimeInfo != null && runtimeInfo.IsOSPlatform(OSPlatform.Linux) &&
Directory.Exists("/dev/dri"))
if (runtimeInfo != null && runtimeInfo.IsOSPlatform(OSPlatform.Linux))
{
ILocalFileSystem localFileSystem = scope.ServiceProvider.GetRequiredService<ILocalFileSystem>();
IMemoryCache memoryCache = scope.ServiceProvider.GetRequiredService<IMemoryCache>();
if (Directory.Exists("/dev/dri"))
{
ILocalFileSystem localFileSystem = scope.ServiceProvider.GetRequiredService<ILocalFileSystem>();
IMemoryCache memoryCache = scope.ServiceProvider.GetRequiredService<IMemoryCache>();
var devices = localFileSystem.ListFiles("/dev/dri")
.Filter(
s => s.StartsWith("/dev/dri/render", StringComparison.OrdinalIgnoreCase)
|| s.StartsWith("/dev/dri/card", StringComparison.OrdinalIgnoreCase))
.ToList();
var devices = localFileSystem.ListFiles("/dev/dri")
.Filter(
s => s.StartsWith("/dev/dri/render", StringComparison.OrdinalIgnoreCase)
|| s.StartsWith("/dev/dri/card", StringComparison.OrdinalIgnoreCase))
.ToList();
memoryCache.Set("ffmpeg.render_devices", devices);
memoryCache.Set("ffmpeg.render_devices", devices);
}
IHardwareCapabilitiesFactory hardwareCapabilitiesFactory =
scope.ServiceProvider.GetRequiredService<IHardwareCapabilitiesFactory>();
if (hardwareCapabilitiesFactory != null)
{
IMemoryCache memoryCache = scope.ServiceProvider.GetRequiredService<IMemoryCache>();
List<string> displays = await hardwareCapabilitiesFactory.GetVaapiDisplays();
memoryCache.Set("ffmpeg.vaapi_displays", displays);
}
}
}
}

4
ErsatzTV/ViewModels/FFmpegProfileEditViewModel.cs

@ -27,6 +27,7 @@ public class FFmpegProfileEditViewModel @@ -27,6 +27,7 @@ public class FFmpegProfileEditViewModel
ScalingBehavior = viewModel.ScalingBehavior;
ThreadCount = viewModel.ThreadCount;
HardwareAcceleration = viewModel.HardwareAcceleration;
VaapiDisplay = viewModel.VaapiDisplay;
VaapiDriver = viewModel.VaapiDriver;
VaapiDevice = viewModel.VaapiDevice;
QsvExtraHardwareFrames = viewModel.QsvExtraHardwareFrames;
@ -53,6 +54,7 @@ public class FFmpegProfileEditViewModel @@ -53,6 +54,7 @@ public class FFmpegProfileEditViewModel
public ScalingBehavior ScalingBehavior { get; set; }
public int ThreadCount { get; set; }
public HardwareAccelerationKind HardwareAcceleration { get; set; }
public string VaapiDisplay { get; set; }
public VaapiDriver VaapiDriver { get; set; }
public string VaapiDevice { get; set; }
public int? QsvExtraHardwareFrames { get; set; }
@ -69,6 +71,7 @@ public class FFmpegProfileEditViewModel @@ -69,6 +71,7 @@ public class FFmpegProfileEditViewModel
Name,
ThreadCount,
HardwareAcceleration,
VaapiDisplay,
VaapiDriver,
VaapiDevice,
QsvExtraHardwareFrames,
@ -97,6 +100,7 @@ public class FFmpegProfileEditViewModel @@ -97,6 +100,7 @@ public class FFmpegProfileEditViewModel
Name,
ThreadCount,
HardwareAcceleration,
VaapiDisplay,
VaapiDriver,
VaapiDevice,
QsvExtraHardwareFrames,

Loading…
Cancel
Save