Browse Source

support radeon vaapi acceleration (#420)

pull/421/head
Jason Dove 4 years ago committed by GitHub
parent
commit
c43ca2837d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      CHANGELOG.md
  2. 4
      ErsatzTV.Application/FFmpegProfiles/Commands/UpdateFFmpegSettingsHandler.cs
  3. 3
      ErsatzTV.Application/FFmpegProfiles/FFmpegProfileViewModel.cs
  4. 5
      ErsatzTV.Application/FFmpegProfiles/FFmpegSettingsViewModel.cs
  5. 2
      ErsatzTV.Application/FFmpegProfiles/Mapper.cs
  6. 4
      ErsatzTV.Application/FFmpegProfiles/Queries/GetFFmpegSettingsHandler.cs
  7. 7
      ErsatzTV.Application/Streaming/Queries/GetPlayoutItemProcessByChannelNumberHandler.cs
  8. 3
      ErsatzTV.Core.Tests/FFmpeg/TranscodingTests.cs
  9. 1
      ErsatzTV.Core/Domain/ConfigElementKey.cs
  10. 9
      ErsatzTV.Core/Domain/FFmpegProfile.cs
  11. 14
      ErsatzTV.Core/FFmpeg/FFmpegProcessBuilder.cs
  12. 5
      ErsatzTV.Core/FFmpeg/FFmpegProcessService.cs
  13. 3
      ErsatzTV.Core/FFmpeg/VaapiDriver.cs
  14. 46
      ErsatzTV.Infrastructure/Health/Checks/VaapiDriverHealthCheck.cs
  15. 3293
      ErsatzTV.Infrastructure/Migrations/20211012191053_Add_FFmpegProfileVaapiDriverVaapiDevice.Designer.cs
  16. 34
      ErsatzTV.Infrastructure/Migrations/20211012191053_Add_FFmpegProfileVaapiDriverVaapiDevice.cs
  17. 3293
      ErsatzTV.Infrastructure/Migrations/20211012202215_Update_FFmpegProfileVaapiDriverVaapiDevice.Designer.cs
  18. 19
      ErsatzTV.Infrastructure/Migrations/20211012202215_Update_FFmpegProfileVaapiDriverVaapiDevice.cs
  19. 6
      ErsatzTV.Infrastructure/Migrations/TvContextModelSnapshot.cs
  20. 1
      ErsatzTV.sln.DotSettings
  21. 28
      ErsatzTV/Pages/FFmpegEditor.razor
  22. 6
      ErsatzTV/Pages/Settings.razor
  23. 18
      ErsatzTV/Services/RunOnce/PlatformSettingsService.cs
  24. 5
      ErsatzTV/ViewModels/FFmpegProfileEditViewModel.cs
  25. 3
      docker/vaapi/ffmpeg.Dockerfile

3
CHANGELOG.md

@ -14,10 +14,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). @@ -14,10 +14,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- "working ahead" means transcoding at full speed, which can take a lot of resources
- This setting must be greater than or equal to 0
- Add more watermark locations ("middle" of each side)
- Add `VAAPI Device` setting to ffmpeg profile to support installations with multiple video cards
- Add `RadeonSI` option for `VAAPI Driver`, and add include mesa drivers in vaapi docker image
### Changed
- Upgrade ffmpeg from 4.3 to 4.4 in all docker images
- Upgrading from 4.3 to 4.4 is recommended for all installations
- Move `VAAPI Driver` from settings page to ffmpeg profile to support installations with multiple video cards
### Fixed
- Fix some transcoding edge cases with nvidia and pixel format `yuv420p10le`

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

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

3
ErsatzTV.Application/FFmpegProfiles/FFmpegProfileViewModel.cs

@ -1,5 +1,6 @@ @@ -1,5 +1,6 @@
using ErsatzTV.Application.Resolutions;
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.FFmpeg;
namespace ErsatzTV.Application.FFmpegProfiles
{
@ -9,6 +10,8 @@ namespace ErsatzTV.Application.FFmpegProfiles @@ -9,6 +10,8 @@ namespace ErsatzTV.Application.FFmpegProfiles
int ThreadCount,
bool Transcode,
HardwareAccelerationKind HardwareAcceleration,
VaapiDriver VaapiDriver,
string VaapiDevice,
ResolutionViewModel Resolution,
bool NormalizeVideo,
string VideoCodec,

5
ErsatzTV.Application/FFmpegProfiles/FFmpegSettingsViewModel.cs

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

2
ErsatzTV.Application/FFmpegProfiles/Mapper.cs

@ -12,6 +12,8 @@ namespace ErsatzTV.Application.FFmpegProfiles @@ -12,6 +12,8 @@ namespace ErsatzTV.Application.FFmpegProfiles
profile.ThreadCount,
profile.Transcode,
profile.HardwareAcceleration,
profile.VaapiDriver,
profile.VaapiDevice,
Project(profile.Resolution),
profile.NormalizeVideo,
profile.VideoCodec,

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

@ -1,7 +1,6 @@ @@ -1,7 +1,6 @@
using System.Threading;
using System.Threading.Tasks;
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.FFmpeg;
using ErsatzTV.Core.Interfaces.Repositories;
using LanguageExt;
using MediatR;
@ -29,8 +28,6 @@ namespace ErsatzTV.Application.FFmpegProfiles.Queries @@ -29,8 +28,6 @@ namespace ErsatzTV.Application.FFmpegProfiles.Queries
await _configElementRepository.GetValue<string>(ConfigElementKey.FFmpegPreferredLanguageCode);
Option<int> watermark =
await _configElementRepository.GetValue<int>(ConfigElementKey.FFmpegGlobalWatermarkId);
Option<int> vaapiDriver =
await _configElementRepository.GetValue<int>(ConfigElementKey.FFmpegVaapiDriver);
Option<int> hlsSegmenterIdleTimeout =
await _configElementRepository.GetValue<int>(ConfigElementKey.FFmpegSegmenterTimeout);
Option<int> workAheadSegmenterLimit =
@ -43,7 +40,6 @@ namespace ErsatzTV.Application.FFmpegProfiles.Queries @@ -43,7 +40,6 @@ namespace ErsatzTV.Application.FFmpegProfiles.Queries
DefaultFFmpegProfileId = await defaultFFmpegProfileId.IfNoneAsync(0),
SaveReports = await saveReports.IfNoneAsync(false),
PreferredLanguageCode = await preferredLanguageCode.IfNoneAsync("eng"),
VaapiDriver = (VaapiDriver)await vaapiDriver.IfNoneAsync(0),
HlsSegmenterIdleTimeout = await hlsSegmenterIdleTimeout.IfNoneAsync(60),
WorkAheadSegmenterLimit = await workAheadSegmenterLimit.IfNoneAsync(1),
};

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

@ -100,10 +100,6 @@ namespace ErsatzTV.Application.Streaming.Queries @@ -100,10 +100,6 @@ namespace ErsatzTV.Application.Streaming.Queries
watermarkId => dbContext.ChannelWatermarks
.SelectOneAsync(w => w.Id, w => w.Id == watermarkId));
Option<VaapiDriver> maybeVaapiDriver = await dbContext.ConfigElements
.GetValue<int>(ConfigElementKey.FFmpegVaapiDriver)
.MapT(i => (VaapiDriver)i);
Process process = await _ffmpegProcessService.ForPlayoutItem(
ffmpegPath,
saveReports,
@ -113,7 +109,8 @@ namespace ErsatzTV.Application.Streaming.Queries @@ -113,7 +109,8 @@ namespace ErsatzTV.Application.Streaming.Queries
playoutItemWithPath.PlayoutItem.StartOffset,
request.StartAtZero ? playoutItemWithPath.PlayoutItem.StartOffset : now,
maybeGlobalWatermark,
maybeVaapiDriver,
channel.FFmpegProfile.VaapiDriver,
channel.FFmpegProfile.VaapiDevice,
request.StartAtZero,
request.HlsRealtime);

3
ErsatzTV.Core.Tests/FFmpeg/TranscodingTests.cs

@ -186,7 +186,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg @@ -186,7 +186,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg
now,
now,
None,
None,
VaapiDriver.Default,
"/dev/dri/renderD128",
false,
false);

1
ErsatzTV.Core/Domain/ConfigElementKey.cs

@ -13,7 +13,6 @@ @@ -13,7 +13,6 @@
public static ConfigElementKey FFmpegSaveReports => new("ffmpeg.save_reports");
public static ConfigElementKey FFmpegPreferredLanguageCode => new("ffmpeg.preferred_language_code");
public static ConfigElementKey FFmpegGlobalWatermarkId => new("ffmpeg.global_watermark_id");
public static ConfigElementKey FFmpegVaapiDriver => new("ffmpeg.vaapi_driver");
public static ConfigElementKey FFmpegSegmenterTimeout => new("ffmpeg.segmenter.timeout_seconds");
public static ConfigElementKey FFmpegWorkAheadSegmenters => new("ffmpeg.segmenter.work_ahead_limit");
public static ConfigElementKey SearchIndexVersion => new("search_index.version");

9
ErsatzTV.Core/Domain/FFmpegProfile.cs

@ -1,4 +1,6 @@ @@ -1,4 +1,6 @@
namespace ErsatzTV.Core.Domain
using ErsatzTV.Core.FFmpeg;
namespace ErsatzTV.Core.Domain
{
public record FFmpegProfile
{
@ -7,6 +9,8 @@ @@ -7,6 +9,8 @@
public int ThreadCount { get; set; }
public bool Transcode { get; set; }
public HardwareAccelerationKind HardwareAcceleration { get; set; }
public VaapiDriver VaapiDriver { get; set; }
public string VaapiDevice { get; set; }
public int ResolutionId { get; set; }
public Resolution Resolution { get; set; }
public string VideoCodec { get; set; }
@ -39,7 +43,8 @@ @@ -39,7 +43,8 @@
AudioChannels = 2,
AudioSampleRate = 48,
NormalizeVideo = true,
NormalizeAudio = true
NormalizeAudio = true,
HardwareAcceleration = HardwareAccelerationKind.None
};
}
}

14
ErsatzTV.Core/FFmpeg/FFmpegProcessBuilder.cs

@ -39,6 +39,7 @@ namespace ErsatzTV.Core.FFmpeg @@ -39,6 +39,7 @@ namespace ErsatzTV.Core.FFmpeg
private FFmpegComplexFilterBuilder _complexFilterBuilder = new();
private bool _isConcat;
private VaapiDriver _vaapiDriver;
private string _vaapiDevice;
private HardwareAccelerationKind _hwAccel;
private string _outputPixelFormat;
@ -49,13 +50,17 @@ namespace ErsatzTV.Core.FFmpeg @@ -49,13 +50,17 @@ namespace ErsatzTV.Core.FFmpeg
_logger = logger;
}
public FFmpegProcessBuilder WithVaapiDriver(Option<VaapiDriver> maybeVaapiDriver)
public FFmpegProcessBuilder WithVaapiDriver(VaapiDriver vaapiDriver, string vaapiDevice)
{
foreach (VaapiDriver vaapiDriver in maybeVaapiDriver)
if (vaapiDriver != VaapiDriver.Default)
{
_vaapiDriver = vaapiDriver;
}
_vaapiDevice = string.IsNullOrWhiteSpace(vaapiDevice)
? "/dev/dri/renderD128"
: vaapiDevice;
return this;
}
@ -96,7 +101,7 @@ namespace ErsatzTV.Core.FFmpeg @@ -96,7 +101,7 @@ namespace ErsatzTV.Core.FFmpeg
_arguments.Add("-hwaccel");
_arguments.Add("vaapi");
_arguments.Add("-vaapi_device");
_arguments.Add("/dev/dri/renderD128");
_arguments.Add(_vaapiDevice);
_arguments.Add("-hwaccel_output_format");
_arguments.Add("vaapi");
break;
@ -522,6 +527,9 @@ namespace ErsatzTV.Core.FFmpeg @@ -522,6 +527,9 @@ namespace ErsatzTV.Core.FFmpeg
case VaapiDriver.iHD:
startInfo.EnvironmentVariables.Add("LIBVA_DRIVER_NAME", "iHD");
break;
case VaapiDriver.RadeonSI:
startInfo.EnvironmentVariables.Add("LIBVA_DRIVER_NAME", "radeonsi");
break;
}
}

5
ErsatzTV.Core/FFmpeg/FFmpegProcessService.cs

@ -39,7 +39,8 @@ namespace ErsatzTV.Core.FFmpeg @@ -39,7 +39,8 @@ namespace ErsatzTV.Core.FFmpeg
DateTimeOffset start,
DateTimeOffset now,
Option<ChannelWatermark> globalWatermark,
Option<VaapiDriver> maybeVaapiDriver,
VaapiDriver vaapiDriver,
string vaapiDevice,
bool startAtZero,
bool hlsRealtime)
{
@ -64,8 +65,8 @@ namespace ErsatzTV.Core.FFmpeg @@ -64,8 +65,8 @@ namespace ErsatzTV.Core.FFmpeg
FFmpegProcessBuilder builder = new FFmpegProcessBuilder(ffmpegPath, saveReports, _logger)
.WithThreads(playbackSettings.ThreadCount)
.WithVaapiDriver(vaapiDriver, vaapiDevice)
.WithHardwareAcceleration(playbackSettings.HardwareAcceleration, videoStream.PixelFormat, playbackSettings.VideoCodec)
.WithVaapiDriver(maybeVaapiDriver)
.WithQuiet()
.WithFormatFlags(playbackSettings.FormatFlags)
.WithRealtimeOutput(playbackSettings.RealtimeOutput)

3
ErsatzTV.Core/FFmpeg/VaapiDriver.cs

@ -7,6 +7,7 @@ namespace ErsatzTV.Core.FFmpeg @@ -7,6 +7,7 @@ namespace ErsatzTV.Core.FFmpeg
{
Default = 0,
iHD = 1,
i965 = 2
i965 = 2,
RadeonSI = 3
}
}

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

@ -1,45 +1,47 @@ @@ -1,45 +1,47 @@
using System;
using System.Reflection;
using System.Collections.Generic;
using System.Linq;
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;
using ErsatzTV.Infrastructure.Data;
using Microsoft.EntityFrameworkCore;
namespace ErsatzTV.Infrastructure.Health.Checks
{
public class VaapiDriverHealthCheck : BaseHealthCheck, IVaapiDriverHealthCheck
{
private readonly IConfigElementRepository _configElementRepository;
private readonly IDbContextFactory<TvContext> _dbContextFactory;
public VaapiDriverHealthCheck(IConfigElementRepository configElementRepository) =>
_configElementRepository = configElementRepository;
public VaapiDriverHealthCheck(IDbContextFactory<TvContext> dbContextFactory)
{
_dbContextFactory = dbContextFactory;
}
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();
}
await using TvContext dbContext = _dbContextFactory.CreateDbContext();
List<FFmpegProfile> profiles = await dbContext.FFmpegProfiles
.Filter(p => p.HardwareAcceleration == HardwareAccelerationKind.Vaapi)
.ToListAsync();
Option<int> maybeVaapiDriver =
await _configElementRepository.GetValue<int>(ConfigElementKey.FFmpegVaapiDriver);
var vaapiDriver = (VaapiDriver)await maybeVaapiDriver.IfNoneAsync(0);
if (vaapiDriver == VaapiDriver.Default)
if (profiles.Count == 0)
{
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 NotApplicableResult();
}
return OkResult();
var defaultProfiles = profiles
.Filter(p => p.VaapiDriver == VaapiDriver.Default)
.ToList();
return defaultProfiles.Any()
? InfoResult(
$"{defaultProfiles.Count} FFmpeg Profile{(defaultProfiles.Count > 1 ? "s are" : " is")} set to use Default VAAPI Driver; selecting iHD (Gen 8+) or i965 (up to Gen 9) may offer better performance with Intel iGPU")
: OkResult();
}
}
}

3293
ErsatzTV.Infrastructure/Migrations/20211012191053_Add_FFmpegProfileVaapiDriverVaapiDevice.Designer.cs generated

File diff suppressed because it is too large Load Diff

34
ErsatzTV.Infrastructure/Migrations/20211012191053_Add_FFmpegProfileVaapiDriverVaapiDevice.cs

@ -0,0 +1,34 @@ @@ -0,0 +1,34 @@
using Microsoft.EntityFrameworkCore.Migrations;
namespace ErsatzTV.Infrastructure.Migrations
{
public partial class Add_FFmpegProfileVaapiDriverVaapiDevice : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "VaapiDevice",
table: "FFmpegProfile",
type: "TEXT",
nullable: true);
migrationBuilder.AddColumn<int>(
name: "VaapiDriver",
table: "FFmpegProfile",
type: "INTEGER",
nullable: false,
defaultValue: 0);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "VaapiDevice",
table: "FFmpegProfile");
migrationBuilder.DropColumn(
name: "VaapiDriver",
table: "FFmpegProfile");
}
}
}

3293
ErsatzTV.Infrastructure/Migrations/20211012202215_Update_FFmpegProfileVaapiDriverVaapiDevice.Designer.cs generated

File diff suppressed because it is too large Load Diff

19
ErsatzTV.Infrastructure/Migrations/20211012202215_Update_FFmpegProfileVaapiDriverVaapiDevice.cs

@ -0,0 +1,19 @@ @@ -0,0 +1,19 @@
using Microsoft.EntityFrameworkCore.Migrations;
namespace ErsatzTV.Infrastructure.Migrations
{
public partial class Update_FFmpegProfileVaapiDriverVaapiDevice : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.Sql("UPDATE FFmpegProfile SET VaapiDevice = '/dev/dri/renderD128'");
migrationBuilder.Sql(
"UPDATE FFmpegProfile SET VaapiDriver = (SELECT IFNULL(Value, 0) FROM ConfigElement WHERE Key = 'ffmpeg.vaapi_driver')");
migrationBuilder.Sql("DELETE FROM ConfigElement WHERE Key = 'ffmpeg.vaapi_driver'");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
}
}
}

6
ErsatzTV.Infrastructure/Migrations/TvContextModelSnapshot.cs

@ -482,6 +482,12 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -482,6 +482,12 @@ namespace ErsatzTV.Infrastructure.Migrations
b.Property<bool>("Transcode")
.HasColumnType("INTEGER");
b.Property<string>("VaapiDevice")
.HasColumnType("TEXT");
b.Property<int>("VaapiDriver")
.HasColumnType("INTEGER");
b.Property<int>("VideoBitrate")
.HasColumnType("INTEGER");

1
ErsatzTV.sln.DotSettings

@ -42,6 +42,7 @@ @@ -42,6 +42,7 @@
<s:Boolean x:Key="/Default/UserDictionary/Words/=playout/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Playouts/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=probesize/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Radeon/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Segmenter/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=setsar/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=showtitle/@EntryIndexedValue">True</s:Boolean>

28
ErsatzTV/Pages/FFmpegEditor.razor

@ -5,10 +5,14 @@ @@ -5,10 +5,14 @@
@using ErsatzTV.Application.FFmpegProfiles
@using ErsatzTV.Application.FFmpegProfiles.Commands
@using ErsatzTV.Application.FFmpegProfiles.Queries
@using ErsatzTV.Core.FFmpeg
@using Microsoft.AspNetCore.Components
@using Microsoft.Extensions.Caching.Memory
@inject NavigationManager _navigationManager
@inject ILogger<FFmpegEditor> _logger
@inject ISnackbar _snackbar
@inject IMediator _mediator
@inject IMemoryCache _memoryCache
<MudContainer MaxWidth="MaxWidth.ExtraLarge" Class="pt-8">
<EditForm EditContext="_editContext" OnSubmit="@HandleSubmitAsync">
@ -58,6 +62,22 @@ @@ -58,6 +62,22 @@
}
</MudSelect>
</MudElement>
<MudElement HtmlTag="div" Class="mt-3">
<MudSelect Disabled="@(_model.HardwareAcceleration != HardwareAccelerationKind.Vaapi)" Label="VAAPI Driver" @bind-Value="_model.VaapiDriver" For="@(() => _model.VaapiDriver)">
@foreach (VaapiDriver driver in Enum.GetValues<VaapiDriver>())
{
<MudSelectItem Value="@driver">@driver</MudSelectItem>
}
</MudSelect>
</MudElement>
<MudElement HtmlTag="div" Class="mt-3">
<MudSelect Disabled="@(_model.HardwareAcceleration != HardwareAccelerationKind.Vaapi)" Label="VAAPI Device" @bind-Value="_model.VaapiDevice" For="@(() => _model.VaapiDevice)">
@foreach (string device in _vaapiDevices)
{
<MudSelectItem Value="@device">@device</MudSelectItem>
}
</MudSelect>
</MudElement>
<MudElement HtmlTag="div" Class="mt-3">
<MudCheckBox Disabled="@(!_model.Transcode)" Label="Normalize Video" @bind-Checked="@_model.NormalizeVideo" For="@(() => _model.NormalizeVideo)"/>
</MudElement>
@ -107,6 +127,7 @@ @@ -107,6 +127,7 @@
private ValidationMessageStore _messageStore;
private List<ResolutionViewModel> _resolutions;
private List<string> _vaapiDevices;
protected override async Task OnParametersSetAsync()
{
@ -126,6 +147,13 @@ @@ -126,6 +147,13 @@
_editContext = new EditContext(_model);
_messageStore = new ValidationMessageStore(_editContext);
if (!_memoryCache.TryGetValue("ffmpeg.render_devices", out List<string> vaapiDevices))
{
vaapiDevices = new List<string> { "/dev/dri/renderD128" };
}
_vaapiDevices = vaapiDevices;
}
private bool IsEdit => Id != 0;

6
ErsatzTV/Pages/Settings.razor

@ -58,12 +58,6 @@ @@ -58,12 +58,6 @@
<MudSelectItem T="int?" Value="@watermark.Id">@watermark.Name</MudSelectItem>
}
</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>
<MudElement HtmlTag="div" Class="mt-3">
<MudTextField T="int"
Label="HLS Segmenter Idle Timeout"

18
ErsatzTV/Services/RunOnce/PlatformSettingsService.cs

@ -1,10 +1,14 @@ @@ -1,10 +1,14 @@
using System.Runtime.InteropServices;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Interfaces.Metadata;
using ErsatzTV.Core.Interfaces.Repositories;
using ErsatzTV.Core.Interfaces.Runtime;
using ErsatzTV.Infrastructure.Data;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
@ -36,6 +40,18 @@ namespace ErsatzTV.Services.RunOnce @@ -36,6 +40,18 @@ namespace ErsatzTV.Services.RunOnce
IConfigElementRepository repo = scope.ServiceProvider.GetRequiredService<IConfigElementRepository>();
await repo.Upsert(ConfigElementKey.FFmpegSaveReports, false);
}
if (runtimeInfo != null && runtimeInfo.IsOSPlatform(OSPlatform.Linux))
{
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"))
.ToList();
memoryCache.Set("ffmpeg.render_devices", devices);
}
}
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;

5
ErsatzTV/ViewModels/FFmpegProfileEditViewModel.cs

@ -2,6 +2,7 @@ @@ -2,6 +2,7 @@
using ErsatzTV.Application.FFmpegProfiles.Commands;
using ErsatzTV.Application.Resolutions;
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.FFmpeg;
namespace ErsatzTV.ViewModels
{
@ -27,6 +28,8 @@ namespace ErsatzTV.ViewModels @@ -27,6 +28,8 @@ namespace ErsatzTV.ViewModels
ThreadCount = viewModel.ThreadCount;
Transcode = viewModel.Transcode;
HardwareAcceleration = viewModel.HardwareAcceleration;
VaapiDriver = viewModel.VaapiDriver;
VaapiDevice = viewModel.VaapiDevice;
VideoBitrate = viewModel.VideoBitrate;
VideoBufferSize = viewModel.VideoBufferSize;
VideoCodec = viewModel.VideoCodec;
@ -46,6 +49,8 @@ namespace ErsatzTV.ViewModels @@ -46,6 +49,8 @@ namespace ErsatzTV.ViewModels
public int ThreadCount { get; set; }
public bool Transcode { get; set; }
public HardwareAccelerationKind HardwareAcceleration { get; set; }
public VaapiDriver VaapiDriver { get; set; }
public string VaapiDevice { get; set; }
public int VideoBitrate { get; set; }
public int VideoBufferSize { get; set; }
public string VideoCodec { get; set; }

3
docker/vaapi/ffmpeg.Dockerfile

@ -12,6 +12,7 @@ RUN apt-get update && DEBIAN_FRONTEND="noninteractive" apt-get install -y libicu @@ -12,6 +12,7 @@ RUN apt-get update && DEBIAN_FRONTEND="noninteractive" apt-get install -y libicu
build-essential \
cmake \
wget \
mesa-va-drivers \
&& mkdir /tmp/intel && cd /tmp/intel \
&& wget -O - https://github.com/intel/libva/archive/refs/tags/2.12.0.tar.gz | tar zxf - \
&& cd libva-2.12.0 \
@ -36,4 +37,4 @@ RUN apt-get update && DEBIAN_FRONTEND="noninteractive" apt-get install -y libicu @@ -36,4 +37,4 @@ RUN apt-get update && DEBIAN_FRONTEND="noninteractive" apt-get install -y libicu
&& apt autoremove -y \
&& rm -rf /tmp/intel \
&& rm -rf /var/lib/apt/lists/* \
&& mv /usr/lib/x86_64-linux-gnu/dri/i* /usr/local/lib/dri/
&& mv /usr/lib/x86_64-linux-gnu/dri/* /usr/local/lib/dri/

Loading…
Cancel
Save