Browse Source

add vaapi driver setting and health check (#377)

* add vaapi driver option

* add vaapi driver setting and health check
pull/378/head
Jason Dove 5 years ago committed by GitHub
parent
commit
f1f50e883c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      ErsatzTV.Application/FFmpegProfiles/Commands/UpdateFFmpegSettingsHandler.cs
  2. 5
      ErsatzTV.Application/FFmpegProfiles/FFmpegSettingsViewModel.cs
  3. 4
      ErsatzTV.Application/FFmpegProfiles/Queries/GetFFmpegSettingsHandler.cs
  4. 10
      ErsatzTV.Application/Health/Queries/GetAllHealthCheckResultsHandler.cs
  5. 7
      ErsatzTV.Application/Streaming/Queries/GetPlayoutItemProcessByChannelNumberHandler.cs
  6. 1
      ErsatzTV.Core/Domain/ConfigElementKey.cs
  7. 27
      ErsatzTV.Core/FFmpeg/FFmpegProcessBuilder.cs
  8. 4
      ErsatzTV.Core/FFmpeg/FFmpegProcessService.cs
  9. 12
      ErsatzTV.Core/FFmpeg/VaapiDriver.cs
  10. 6
      ErsatzTV.Core/Health/Checks/IVaapiDriverHealthCheck.cs
  11. 9
      ErsatzTV.Core/Health/HealthCheckStatus.cs
  12. 3
      ErsatzTV.Infrastructure/Health/Checks/BaseHealthCheck.cs
  13. 45
      ErsatzTV.Infrastructure/Health/Checks/VaapiDriverHealthCheck.cs
  14. 6
      ErsatzTV.Infrastructure/Health/HealthCheckService.cs
  15. 7
      ErsatzTV/Pages/Settings.razor
  16. 1
      ErsatzTV/Startup.cs

4
ErsatzTV.Application/FFmpegProfiles/Commands/UpdateFFmpegSettingsHandler.cs

@ -115,6 +115,10 @@ namespace ErsatzTV.Application.FFmpegProfiles.Commands
await _configElementRepository.Delete(ConfigElementKey.FFmpegGlobalWatermarkId); await _configElementRepository.Delete(ConfigElementKey.FFmpegGlobalWatermarkId);
} }
await _configElementRepository.Upsert(
ConfigElementKey.FFmpegVaapiDriver,
(int)request.Settings.VaapiDriver);
return Unit.Default; return Unit.Default;
} }
} }

5
ErsatzTV.Application/FFmpegProfiles/FFmpegSettingsViewModel.cs

@ -1,4 +1,6 @@
namespace ErsatzTV.Application.FFmpegProfiles using ErsatzTV.Core.FFmpeg;
namespace ErsatzTV.Application.FFmpegProfiles
{ {
public class FFmpegSettingsViewModel public class FFmpegSettingsViewModel
{ {
@ -8,5 +10,6 @@
public string PreferredLanguageCode { get; set; } public string PreferredLanguageCode { get; set; }
public bool SaveReports { get; set; } public bool SaveReports { get; set; }
public int? GlobalWatermarkId { get; set; } public int? GlobalWatermarkId { get; set; }
public VaapiDriver VaapiDriver { get; set; }
} }
} }

4
ErsatzTV.Application/FFmpegProfiles/Queries/GetFFmpegSettingsHandler.cs

@ -1,6 +1,7 @@
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using ErsatzTV.Core.Domain; using ErsatzTV.Core.Domain;
using ErsatzTV.Core.FFmpeg;
using ErsatzTV.Core.Interfaces.Repositories; using ErsatzTV.Core.Interfaces.Repositories;
using LanguageExt; using LanguageExt;
using MediatR; using MediatR;
@ -28,6 +29,8 @@ namespace ErsatzTV.Application.FFmpegProfiles.Queries
await _configElementRepository.GetValue<string>(ConfigElementKey.FFmpegPreferredLanguageCode); await _configElementRepository.GetValue<string>(ConfigElementKey.FFmpegPreferredLanguageCode);
Option<int> watermark = Option<int> watermark =
await _configElementRepository.GetValue<int>(ConfigElementKey.FFmpegGlobalWatermarkId); await _configElementRepository.GetValue<int>(ConfigElementKey.FFmpegGlobalWatermarkId);
Option<int> vaapiDriver =
await _configElementRepository.GetValue<int>(ConfigElementKey.FFmpegVaapiDriver);
var result = new FFmpegSettingsViewModel var result = new FFmpegSettingsViewModel
{ {
@ -36,6 +39,7 @@ namespace ErsatzTV.Application.FFmpegProfiles.Queries
DefaultFFmpegProfileId = await defaultFFmpegProfileId.IfNoneAsync(0), DefaultFFmpegProfileId = await defaultFFmpegProfileId.IfNoneAsync(0),
SaveReports = await saveReports.IfNoneAsync(false), SaveReports = await saveReports.IfNoneAsync(false),
PreferredLanguageCode = await preferredLanguageCode.IfNoneAsync("eng"), PreferredLanguageCode = await preferredLanguageCode.IfNoneAsync("eng"),
VaapiDriver = (VaapiDriver)await vaapiDriver.IfNoneAsync(0)
}; };
foreach (int watermarkId in watermark) foreach (int watermarkId in watermark)

10
ErsatzTV.Application/Health/Queries/GetAllHealthCheckResultsHandler.cs

@ -1,4 +1,5 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using ErsatzTV.Core.Health; using ErsatzTV.Core.Health;
@ -13,9 +14,12 @@ namespace ErsatzTV.Application.Health.Queries
public GetAllHealthCheckResultsHandler(IHealthCheckService healthCheckService) => public GetAllHealthCheckResultsHandler(IHealthCheckService healthCheckService) =>
_healthCheckService = healthCheckService; _healthCheckService = healthCheckService;
public Task<List<HealthCheckResult>> Handle( public async Task<List<HealthCheckResult>> Handle(
GetAllHealthCheckResults request, GetAllHealthCheckResults request,
CancellationToken cancellationToken) => CancellationToken cancellationToken)
_healthCheckService.PerformHealthChecks(); {
List<HealthCheckResult> results = await _healthCheckService.PerformHealthChecks();
return results.Filter(r => r.Status != HealthCheckStatus.NotApplicable).ToList();
}
} }
} }

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

@ -99,6 +99,10 @@ namespace ErsatzTV.Application.Streaming.Queries
watermarkId => dbContext.ChannelWatermarks watermarkId => dbContext.ChannelWatermarks
.SelectOneAsync(w => w.Id, w => w.Id == watermarkId)); .SelectOneAsync(w => w.Id, w => w.Id == watermarkId));
Option<VaapiDriver> maybeVaapiDriver = await dbContext.ConfigElements
.GetValue<int>(ConfigElementKey.FFmpegVaapiDriver)
.MapT(i => (VaapiDriver)i);
return Right<BaseError, Process>( return Right<BaseError, Process>(
await _ffmpegProcessService.ForPlayoutItem( await _ffmpegProcessService.ForPlayoutItem(
ffmpegPath, ffmpegPath,
@ -108,7 +112,8 @@ namespace ErsatzTV.Application.Streaming.Queries
playoutItemWithPath.Path, playoutItemWithPath.Path,
playoutItemWithPath.PlayoutItem.StartOffset, playoutItemWithPath.PlayoutItem.StartOffset,
now, now,
maybeGlobalWatermark)); maybeGlobalWatermark,
maybeVaapiDriver));
}, },
async error => async error =>
{ {

1
ErsatzTV.Core/Domain/ConfigElementKey.cs

@ -13,6 +13,7 @@
public static ConfigElementKey FFmpegSaveReports => new("ffmpeg.save_reports"); public static ConfigElementKey FFmpegSaveReports => new("ffmpeg.save_reports");
public static ConfigElementKey FFmpegPreferredLanguageCode => new("ffmpeg.preferred_language_code"); public static ConfigElementKey FFmpegPreferredLanguageCode => new("ffmpeg.preferred_language_code");
public static ConfigElementKey FFmpegGlobalWatermarkId => new("ffmpeg.global_watermark_id"); public static ConfigElementKey FFmpegGlobalWatermarkId => new("ffmpeg.global_watermark_id");
public static ConfigElementKey FFmpegVaapiDriver => new("ffmpeg.vaapi_driver");
public static ConfigElementKey SearchIndexVersion => new("search_index.version"); public static ConfigElementKey SearchIndexVersion => new("search_index.version");
public static ConfigElementKey HDHRTunerCount => new("hdhr.tuner_count"); public static ConfigElementKey HDHRTunerCount => new("hdhr.tuner_count");
public static ConfigElementKey ChannelsPageSize => new("pages.channels.page_size"); public static ConfigElementKey ChannelsPageSize => new("pages.channels.page_size");

27
ErsatzTV.Core/FFmpeg/FFmpegProcessBuilder.cs

@ -43,6 +43,8 @@ namespace ErsatzTV.Core.FFmpeg
private readonly bool _saveReports; private readonly bool _saveReports;
private FFmpegComplexFilterBuilder _complexFilterBuilder = new(); private FFmpegComplexFilterBuilder _complexFilterBuilder = new();
private bool _isConcat; private bool _isConcat;
private VaapiDriver _vaapiDriver;
private HardwareAccelerationKind _hwAccel;
public FFmpegProcessBuilder(string ffmpegPath, bool saveReports) public FFmpegProcessBuilder(string ffmpegPath, bool saveReports)
{ {
@ -50,6 +52,16 @@ namespace ErsatzTV.Core.FFmpeg
_saveReports = saveReports; _saveReports = saveReports;
} }
public FFmpegProcessBuilder WithVaapiDriver(Option<VaapiDriver> maybeVaapiDriver)
{
foreach (VaapiDriver vaapiDriver in maybeVaapiDriver)
{
_vaapiDriver = vaapiDriver;
}
return this;
}
public FFmpegProcessBuilder WithThreads(int threads) public FFmpegProcessBuilder WithThreads(int threads)
{ {
_arguments.Add("-threads"); _arguments.Add("-threads");
@ -59,6 +71,8 @@ namespace ErsatzTV.Core.FFmpeg
public FFmpegProcessBuilder WithHardwareAcceleration(HardwareAccelerationKind hwAccel) public FFmpegProcessBuilder WithHardwareAcceleration(HardwareAccelerationKind hwAccel)
{ {
_hwAccel = hwAccel;
switch (hwAccel) switch (hwAccel)
{ {
case HardwareAccelerationKind.Qsv: case HardwareAccelerationKind.Qsv:
@ -433,6 +447,19 @@ namespace ErsatzTV.Core.FFmpeg
StandardOutputEncoding = Encoding.UTF8 StandardOutputEncoding = Encoding.UTF8
}; };
if (_hwAccel == HardwareAccelerationKind.Vaapi)
{
switch (_vaapiDriver)
{
case VaapiDriver.i965:
startInfo.EnvironmentVariables.Add("LIBVA_DRIVER_NAME", "i965");
break;
case VaapiDriver.iHD:
startInfo.EnvironmentVariables.Add("LIBVA_DRIVER_NAME", "iHD");
break;
}
}
if (_saveReports) if (_saveReports)
{ {
string fileName = _isConcat string fileName = _isConcat

4
ErsatzTV.Core/FFmpeg/FFmpegProcessService.cs

@ -34,7 +34,8 @@ namespace ErsatzTV.Core.FFmpeg
string path, string path,
DateTimeOffset start, DateTimeOffset start,
DateTimeOffset now, DateTimeOffset now,
Option<ChannelWatermark> globalWatermark) Option<ChannelWatermark> globalWatermark,
Option<VaapiDriver> maybeVaapiDriver)
{ {
MediaStream videoStream = await _ffmpegStreamSelector.SelectVideoStream(channel, version); MediaStream videoStream = await _ffmpegStreamSelector.SelectVideoStream(channel, version);
Option<MediaStream> maybeAudioStream = await _ffmpegStreamSelector.SelectAudioStream(channel, version); Option<MediaStream> maybeAudioStream = await _ffmpegStreamSelector.SelectAudioStream(channel, version);
@ -58,6 +59,7 @@ namespace ErsatzTV.Core.FFmpeg
FFmpegProcessBuilder builder = new FFmpegProcessBuilder(ffmpegPath, saveReports) FFmpegProcessBuilder builder = new FFmpegProcessBuilder(ffmpegPath, saveReports)
.WithThreads(playbackSettings.ThreadCount) .WithThreads(playbackSettings.ThreadCount)
.WithHardwareAcceleration(playbackSettings.HardwareAcceleration) .WithHardwareAcceleration(playbackSettings.HardwareAcceleration)
.WithVaapiDriver(maybeVaapiDriver)
.WithQuiet() .WithQuiet()
.WithFormatFlags(playbackSettings.FormatFlags) .WithFormatFlags(playbackSettings.FormatFlags)
.WithRealtimeOutput(playbackSettings.RealtimeOutput) .WithRealtimeOutput(playbackSettings.RealtimeOutput)

12
ErsatzTV.Core/FFmpeg/VaapiDriver.cs

@ -0,0 +1,12 @@
using System.Diagnostics.CodeAnalysis;
namespace ErsatzTV.Core.FFmpeg
{
[SuppressMessage("ReSharper", "InconsistentNaming")]
public enum VaapiDriver
{
Default = 0,
iHD = 1,
i965 = 2
}
}

6
ErsatzTV.Core/Health/Checks/IVaapiDriverHealthCheck.cs

@ -0,0 +1,6 @@
namespace ErsatzTV.Core.Health.Checks
{
public interface IVaapiDriverHealthCheck : IHealthCheck
{
}
}

9
ErsatzTV.Core/Health/HealthCheckStatus.cs

@ -2,9 +2,10 @@
{ {
public enum HealthCheckStatus public enum HealthCheckStatus
{ {
Pass, Pass = 0,
Fail, Fail = 1,
Warning, Warning = 2,
Info Info = 3,
NotApplicable = 4
} }
} }

3
ErsatzTV.Infrastructure/Health/Checks/BaseHealthCheck.cs

@ -14,6 +14,9 @@ namespace ErsatzTV.Infrastructure.Health.Checks
protected HealthCheckResult Result(HealthCheckStatus status, string message) => protected HealthCheckResult Result(HealthCheckStatus status, string message) =>
new(Title, status, message); new(Title, status, message);
protected HealthCheckResult NotApplicableResult() =>
new(Title, HealthCheckStatus.NotApplicable, string.Empty);
protected HealthCheckResult OkResult() => protected HealthCheckResult OkResult() =>
new(Title, HealthCheckStatus.Pass, string.Empty); new(Title, HealthCheckStatus.Pass, string.Empty);

45
ErsatzTV.Infrastructure/Health/Checks/VaapiDriverHealthCheck.cs

@ -0,0 +1,45 @@
using System;
using System.Reflection;
using System.Threading.Tasks;
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.FFmpeg;
using ErsatzTV.Core.Health;
using ErsatzTV.Core.Health.Checks;
using ErsatzTV.Core.Interfaces.Repositories;
using LanguageExt;
namespace ErsatzTV.Infrastructure.Health.Checks
{
public class VaapiDriverHealthCheck : BaseHealthCheck, IVaapiDriverHealthCheck
{
private readonly IConfigElementRepository _configElementRepository;
public VaapiDriverHealthCheck(IConfigElementRepository configElementRepository) =>
_configElementRepository = configElementRepository;
protected override string Title => "VAAPI Driver";
public async Task<HealthCheckResult> Check()
{
string version = Assembly.GetEntryAssembly()?.GetCustomAttribute<AssemblyInformationalVersionAttribute>()
?.InformationalVersion ?? "unknown";
if (!version.Contains("docker", StringComparison.OrdinalIgnoreCase) ||
!version.Contains("vaapi", StringComparison.OrdinalIgnoreCase))
{
return NotApplicableResult();
}
Option<int> maybeVaapiDriver =
await _configElementRepository.GetValue<int>(ConfigElementKey.FFmpegVaapiDriver);
var vaapiDriver = (VaapiDriver)await maybeVaapiDriver.IfNoneAsync(0);
if (vaapiDriver == VaapiDriver.Default)
{
return InfoResult(
"Settings > FFmpeg Settings > VAAPI Driver is set to Default; selecting iHD (Gen 8+) or i965 (up to Gen 9) may offer better performance");
}
return OkResult();
}
}
}

6
ErsatzTV.Infrastructure/Health/HealthCheckService.cs

@ -18,7 +18,8 @@ namespace ErsatzTV.Infrastructure.Health
IHardwareAccelerationHealthCheck hardwareAccelerationHealthCheck, IHardwareAccelerationHealthCheck hardwareAccelerationHealthCheck,
IMovieMetadataHealthCheck movieMetadataHealthCheck, IMovieMetadataHealthCheck movieMetadataHealthCheck,
IEpisodeMetadataHealthCheck episodeMetadataHealthCheck, IEpisodeMetadataHealthCheck episodeMetadataHealthCheck,
IZeroDurationHealthCheck zeroDurationHealthCheck) IZeroDurationHealthCheck zeroDurationHealthCheck,
IVaapiDriverHealthCheck vaapiDriverHealthCheck)
{ {
_checks = new List<IHealthCheck> _checks = new List<IHealthCheck>
{ {
@ -27,7 +28,8 @@ namespace ErsatzTV.Infrastructure.Health
hardwareAccelerationHealthCheck, hardwareAccelerationHealthCheck,
movieMetadataHealthCheck, movieMetadataHealthCheck,
episodeMetadataHealthCheck, episodeMetadataHealthCheck,
zeroDurationHealthCheck zeroDurationHealthCheck,
vaapiDriverHealthCheck
}; };
} }

7
ErsatzTV/Pages/Settings.razor

@ -11,6 +11,7 @@
@using ErsatzTV.Application.Configuration.Commands @using ErsatzTV.Application.Configuration.Commands
@using ErsatzTV.Application.Watermarks @using ErsatzTV.Application.Watermarks
@using ErsatzTV.Application.Watermarks.Queries @using ErsatzTV.Application.Watermarks.Queries
@using ErsatzTV.Core.FFmpeg
@using Microsoft.AspNetCore.Components @using Microsoft.AspNetCore.Components
@inject IMediator _mediator @inject IMediator _mediator
@inject ISnackbar _snackbar @inject ISnackbar _snackbar
@ -57,6 +58,12 @@
<MudSelectItem T="int?" Value="@watermark.Id">@watermark.Name</MudSelectItem> <MudSelectItem T="int?" Value="@watermark.Id">@watermark.Name</MudSelectItem>
} }
</MudSelect> </MudSelect>
<MudSelect Class="mt-3" Label="VAAPI Driver" @bind-Value="_ffmpegSettings.VaapiDriver" For="@(() => _ffmpegSettings.VaapiDriver)">
@foreach (VaapiDriver driver in Enum.GetValues<VaapiDriver>())
{
<MudSelectItem Value="@driver">@driver</MudSelectItem>
}
</MudSelect>
</MudForm> </MudForm>
</MudCardContent> </MudCardContent>
<MudCardActions> <MudCardActions>

1
ErsatzTV/Startup.cs

@ -208,6 +208,7 @@ namespace ErsatzTV
services.AddScoped<IMovieMetadataHealthCheck, MovieMetadataHealthCheck>(); services.AddScoped<IMovieMetadataHealthCheck, MovieMetadataHealthCheck>();
services.AddScoped<IEpisodeMetadataHealthCheck, EpisodeMetadataHealthCheck>(); services.AddScoped<IEpisodeMetadataHealthCheck, EpisodeMetadataHealthCheck>();
services.AddScoped<IZeroDurationHealthCheck, ZeroDurationHealthCheck>(); services.AddScoped<IZeroDurationHealthCheck, ZeroDurationHealthCheck>();
services.AddScoped<IVaapiDriverHealthCheck, VaapiDriverHealthCheck>();
services.AddScoped<IHealthCheckService, HealthCheckService>(); services.AddScoped<IHealthCheckService, HealthCheckService>();
services.AddScoped<IChannelRepository, ChannelRepository>(); services.AddScoped<IChannelRepository, ChannelRepository>();

Loading…
Cancel
Save