Browse Source

detect avisynth demuxer

pull/2612/head
Jason Dove 9 months ago
parent
commit
d1b83f167b
No known key found for this signature in database
  1. 6
      ErsatzTV.Application/Troubleshooting/Queries/GetTroubleshootingInfoHandler.cs
  2. 1
      ErsatzTV.Application/Troubleshooting/TroubleshootingInfo.cs
  3. 19
      ErsatzTV.FFmpeg.Tests/PipelineBuilderBaseTests.cs
  4. 47
      ErsatzTV.FFmpeg/Capabilities/FFmpegCapabilities.cs
  5. 9
      ErsatzTV.FFmpeg/Capabilities/FFmpegKnownFilter.cs
  6. 19
      ErsatzTV.FFmpeg/Capabilities/FFmpegKnownFormat.cs
  7. 40
      ErsatzTV.FFmpeg/Capabilities/HardwareCapabilitiesFactory.cs
  8. 1
      ErsatzTV.FFmpeg/Capabilities/IFFmpegCapabilities.cs
  9. 4
      ErsatzTV/Pages/Troubleshooting/Troubleshooting.razor

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

@ -73,6 +73,8 @@ public class GetTroubleshootingInfoHandler : IRequestHandler<GetTroubleshootingI @@ -73,6 +73,8 @@ public class GetTroubleshootingInfoHandler : IRequestHandler<GetTroubleshootingI
.AsNoTracking()
.ToListAsync(cancellationToken);
bool aviSynthDemuxer = false;
string nvidiaCapabilities = null;
StringBuilder qsvCapabilities = new();
StringBuilder vaapiCapabilities = new();
@ -157,6 +159,9 @@ public class GetTroubleshootingInfoHandler : IRequestHandler<GetTroubleshootingI @@ -157,6 +159,9 @@ public class GetTroubleshootingInfoHandler : IRequestHandler<GetTroubleshootingI
videoToolboxCapabilities.AppendLine();
videoToolboxCapabilities.AppendLine();
}
var ffmpegCapabilities = await _hardwareCapabilitiesFactory.GetFFmpegCapabilities(ffmpegPath.Value);
aviSynthDemuxer = ffmpegCapabilities.HasDemuxFormat(FFmpegKnownFormat.AviSynth);
}
}
@ -189,6 +194,7 @@ public class GetTroubleshootingInfoHandler : IRequestHandler<GetTroubleshootingI @@ -189,6 +194,7 @@ public class GetTroubleshootingInfoHandler : IRequestHandler<GetTroubleshootingI
ffmpegProfiles,
channels,
channelWatermarks,
aviSynthDemuxer,
nvidiaCapabilities,
qsvCapabilities.ToString(),
vaapiCapabilities.ToString(),

1
ErsatzTV.Application/Troubleshooting/TroubleshootingInfo.cs

@ -14,6 +14,7 @@ public record TroubleshootingInfo( @@ -14,6 +14,7 @@ public record TroubleshootingInfo(
List<FFmpegProfile> FFmpegProfiles,
List<Channel> Channels,
List<ChannelWatermark> Watermarks,
bool AviSynthDemuxer,
string NvidiaCapabilities,
string QsvCapabilities,
string VaapiCapabilities,

19
ErsatzTV.FFmpeg.Tests/PipelineBuilderBaseTests.cs

@ -544,16 +544,11 @@ public class PipelineBuilderBaseTests @@ -544,16 +544,11 @@ public class PipelineBuilderBaseTests
return command;
}
public class DefaultFFmpegCapabilities : FFmpegCapabilities
{
public DefaultFFmpegCapabilities()
: base(
new System.Collections.Generic.HashSet<string>(),
new System.Collections.Generic.HashSet<string>(),
new System.Collections.Generic.HashSet<string>(),
new System.Collections.Generic.HashSet<string>(),
new System.Collections.Generic.HashSet<string>())
{
}
}
public class DefaultFFmpegCapabilities() : FFmpegCapabilities(
new System.Collections.Generic.HashSet<string>(),
new System.Collections.Generic.HashSet<string>(),
new System.Collections.Generic.HashSet<string>(),
new System.Collections.Generic.HashSet<string>(),
new System.Collections.Generic.HashSet<string>(),
new System.Collections.Generic.HashSet<string>());
}

47
ErsatzTV.FFmpeg/Capabilities/FFmpegCapabilities.cs

@ -3,34 +3,21 @@ using ErsatzTV.FFmpeg.Format; @@ -3,34 +3,21 @@ using ErsatzTV.FFmpeg.Format;
namespace ErsatzTV.FFmpeg.Capabilities;
public class FFmpegCapabilities : IFFmpegCapabilities
public class FFmpegCapabilities(
IReadOnlySet<string> ffmpegHardwareAccelerations,
IReadOnlySet<string> ffmpegDecoders,
IReadOnlySet<string> ffmpegFilters,
IReadOnlySet<string> ffmpegEncoders,
IReadOnlySet<string> ffmpegOptions,
IReadOnlySet<string> ffmpegDemuxFormats)
: IFFmpegCapabilities
{
private readonly IReadOnlySet<string> _ffmpegDecoders;
private readonly IReadOnlySet<string> _ffmpegEncoders;
private readonly IReadOnlySet<string> _ffmpegFilters;
private readonly IReadOnlySet<string> _ffmpegHardwareAccelerations;
private readonly IReadOnlySet<string> _ffmpegOptions;
public FFmpegCapabilities(
IReadOnlySet<string> ffmpegHardwareAccelerations,
IReadOnlySet<string> ffmpegDecoders,
IReadOnlySet<string> ffmpegFilters,
IReadOnlySet<string> ffmpegEncoders,
IReadOnlySet<string> ffmpegOptions)
{
_ffmpegHardwareAccelerations = ffmpegHardwareAccelerations;
_ffmpegDecoders = ffmpegDecoders;
_ffmpegFilters = ffmpegFilters;
_ffmpegEncoders = ffmpegEncoders;
_ffmpegOptions = ffmpegOptions;
}
public bool HasHardwareAcceleration(HardwareAccelerationMode hardwareAccelerationMode)
{
// AMF isn't a "hwaccel" in ffmpeg, so check for presence of encoders
if (hardwareAccelerationMode is HardwareAccelerationMode.Amf)
{
return _ffmpegEncoders.Any(e => e.EndsWith(
return ffmpegEncoders.Any(e => e.EndsWith(
$"_{FFmpegKnownHardwareAcceleration.Amf.Name}",
StringComparison.OrdinalIgnoreCase));
}
@ -38,7 +25,7 @@ public class FFmpegCapabilities : IFFmpegCapabilities @@ -38,7 +25,7 @@ public class FFmpegCapabilities : IFFmpegCapabilities
// V4l2m2m isn't a "hwaccel" in ffmpeg, so check for presence of encoders
if (hardwareAccelerationMode is HardwareAccelerationMode.V4l2m2m)
{
return _ffmpegEncoders.Any(e => e.EndsWith(
return ffmpegEncoders.Any(e => e.EndsWith(
$"_{FFmpegKnownHardwareAcceleration.V4l2m2m.Name}",
StringComparison.OrdinalIgnoreCase));
}
@ -57,19 +44,21 @@ public class FFmpegCapabilities : IFFmpegCapabilities @@ -57,19 +44,21 @@ public class FFmpegCapabilities : IFFmpegCapabilities
foreach (FFmpegKnownHardwareAcceleration accelToCheck in maybeAccelToCheck)
{
return _ffmpegHardwareAccelerations.Contains(accelToCheck.Name);
return ffmpegHardwareAccelerations.Contains(accelToCheck.Name);
}
return false;
}
public bool HasDecoder(FFmpegKnownDecoder decoder) => _ffmpegDecoders.Contains(decoder.Name);
public bool HasDecoder(FFmpegKnownDecoder decoder) => ffmpegDecoders.Contains(decoder.Name);
public bool HasEncoder(FFmpegKnownEncoder encoder) => ffmpegEncoders.Contains(encoder.Name);
public bool HasEncoder(FFmpegKnownEncoder encoder) => _ffmpegEncoders.Contains(encoder.Name);
public bool HasFilter(FFmpegKnownFilter filter) => ffmpegFilters.Contains(filter.Name);
public bool HasFilter(FFmpegKnownFilter filter) => _ffmpegFilters.Contains(filter.Name);
public bool HasOption(FFmpegKnownOption ffmpegOption) => ffmpegOptions.Contains(ffmpegOption.Name);
public bool HasOption(FFmpegKnownOption ffmpegOption) => _ffmpegOptions.Contains(ffmpegOption.Name);
public bool HasDemuxFormat(FFmpegKnownFormat format) => ffmpegDemuxFormats.Contains(format.Name);
public Option<IDecoder> SoftwareDecoderForVideoFormat(string videoFormat) =>
videoFormat switch
@ -83,7 +72,7 @@ public class FFmpegCapabilities : IFFmpegCapabilities @@ -83,7 +72,7 @@ public class FFmpegCapabilities : IFFmpegCapabilities
VideoFormat.MsMpeg4V3 => new DecoderMsMpeg4V3(),
VideoFormat.Mpeg4 => new DecoderMpeg4(),
VideoFormat.Vp9 => new DecoderVp9(),
VideoFormat.Av1 => new DecoderAv1(_ffmpegDecoders),
VideoFormat.Av1 => new DecoderAv1(ffmpegDecoders),
VideoFormat.Raw => new DecoderRawVideo(),
VideoFormat.Undetermined => new DecoderImplicit(),

9
ErsatzTV.FFmpeg/Capabilities/FFmpegKnownFilter.cs

@ -11,9 +11,8 @@ public record FFmpegKnownFilter @@ -11,9 +11,8 @@ public record FFmpegKnownFilter
public string Name { get; }
public static IList<string> AllFilters =>
new[]
{
ScaleNpp.Name,
TonemapOpenCL.Name
};
[
ScaleNpp.Name,
TonemapOpenCL.Name
];
}

19
ErsatzTV.FFmpeg/Capabilities/FFmpegKnownFormat.cs

@ -0,0 +1,19 @@ @@ -0,0 +1,19 @@
using System.Diagnostics.CodeAnalysis;
namespace ErsatzTV.FFmpeg.Capabilities;
[SuppressMessage("ReSharper", "IdentifierTypo")]
[SuppressMessage("ReSharper", "StringLiteralTypo")]
public record FFmpegKnownFormat
{
public static readonly FFmpegKnownFormat AviSynth = new("avisynth");
private FFmpegKnownFormat(string Name) => this.Name = Name;
public string Name { get; }
public static IList<string> AllFormats =>
[
AviSynth.Name
];
}

40
ErsatzTV.FFmpeg/Capabilities/HardwareCapabilitiesFactory.cs

@ -72,12 +72,16 @@ public class HardwareCapabilitiesFactory : IHardwareCapabilitiesFactory @@ -72,12 +72,16 @@ public class HardwareCapabilitiesFactory : IHardwareCapabilitiesFactory
IReadOnlySet<string> ffmpegOptions = await GetFFmpegOptions(ffmpegPath)
.Map(set => set.Intersect(FFmpegKnownOption.AllOptions).ToImmutableHashSet());
IReadOnlySet<string> ffmpegDemuxFormats = await GetFFmpegFormats(ffmpegPath, "D")
.Map(set => set.Intersect(FFmpegKnownFormat.AllFormats).ToImmutableHashSet());
return new FFmpegCapabilities(
ffmpegHardwareAccelerations,
ffmpegDecoders,
ffmpegFilters,
ffmpegEncoders,
ffmpegOptions);
ffmpegOptions,
ffmpegDemuxFormats);
}
public async Task<IHardwareCapabilities> GetHardwareCapabilities(
@ -358,6 +362,33 @@ public class HardwareCapabilitiesFactory : IHardwareCapabilitiesFactory @@ -358,6 +362,33 @@ public class HardwareCapabilitiesFactory : IHardwareCapabilitiesFactory
.ToImmutableHashSet();
}
private async Task<IReadOnlySet<string>> GetFFmpegFormats(string ffmpegPath, string muxDemux)
{
var cacheKey = string.Format(CultureInfo.InvariantCulture, FFmpegCapabilitiesCacheKeyFormat, "formats");
if (_memoryCache.TryGetValue(cacheKey, out IReadOnlySet<string>? cachedCapabilities) &&
cachedCapabilities is not null)
{
return cachedCapabilities;
}
string[] arguments = { "-hide_banner", "-formats" };
BufferedCommandResult result = await Cli.Wrap(ffmpegPath)
.WithArguments(arguments)
.WithValidation(CommandResultValidation.None)
.ExecuteBufferedAsync(Encoding.UTF8);
string output = string.IsNullOrWhiteSpace(result.StandardOutput)
? result.StandardError
: result.StandardOutput;
return output.Split("\n").Map(s => s.Trim())
.Bind(l => ParseFFmpegFormatLine(l))
.Where(tuple => tuple.Item1.Contains(muxDemux))
.Map(tuple => tuple.Item2)
.ToImmutableHashSet();
}
private static Option<string> ParseFFmpegAccelLine(string input)
{
const string PATTERN = @"^([\w]+)$";
@ -379,6 +410,13 @@ public class HardwareCapabilitiesFactory : IHardwareCapabilitiesFactory @@ -379,6 +410,13 @@ public class HardwareCapabilitiesFactory : IHardwareCapabilitiesFactory
return match.Success ? match.Groups[1].Value : Option<string>.None;
}
private static Option<Tuple<string, string>> ParseFFmpegFormatLine(string input)
{
const string PATTERN = @"([DE]+)\s+(\w+)";
Match match = Regex.Match(input, PATTERN);
return match.Success ? Tuple(match.Groups[1].Value, match.Groups[2].Value) : Option<Tuple<string, string>>.None;
}
private async Task<IHardwareCapabilities> GetVaapiCapabilities(
Option<string> vaapiDisplay,
Option<string> vaapiDriver,

1
ErsatzTV.FFmpeg/Capabilities/IFFmpegCapabilities.cs

@ -9,5 +9,6 @@ public interface IFFmpegCapabilities @@ -9,5 +9,6 @@ public interface IFFmpegCapabilities
bool HasEncoder(FFmpegKnownEncoder encoder);
bool HasFilter(FFmpegKnownFilter filter);
bool HasOption(FFmpegKnownOption ffmpegOption);
bool HasDemuxFormat(FFmpegKnownFormat format);
Option<IDecoder> SoftwareDecoderForVideoFormat(string videoFormat);
}

4
ErsatzTV/Pages/Troubleshooting/Troubleshooting.razor

@ -157,6 +157,10 @@ @@ -157,6 +157,10 @@
info.VideoControllers,
info.Health,
info.FFmpegSettings,
AviSynth = new
{
Demuxer = info.AviSynthDemuxer
},
info.Channels,
info.FFmpegProfiles
},

Loading…
Cancel
Save