Browse Source

allow older ffmpeg for testing (#1485)

* allow older ffmpeg for testing

* use proper option name
pull/1486/head
Jason Dove 2 years ago committed by GitHub
parent
commit
0550c60a78
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      ErsatzTV.FFmpeg.Tests/PipelineBuilderBaseTests.cs
  2. 7
      ErsatzTV.FFmpeg/Capabilities/FFmpegCapabilities.cs
  3. 42
      ErsatzTV.FFmpeg/Capabilities/HardwareCapabilitiesFactory.cs
  4. 1
      ErsatzTV.FFmpeg/Capabilities/IFFmpegCapabilities.cs
  5. 22
      ErsatzTV.FFmpeg/InputOption/ReadrateInputOption.cs
  6. 8
      ErsatzTV.FFmpeg/Pipeline/PipelineBuilderBase.cs

1
ErsatzTV.FFmpeg.Tests/PipelineBuilderBaseTests.cs

@ -497,6 +497,7 @@ public class PipelineBuilderBaseTests @@ -497,6 +497,7 @@ public class PipelineBuilderBaseTests
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>())
{
}

7
ErsatzTV.FFmpeg/Capabilities/FFmpegCapabilities.cs

@ -9,17 +9,20 @@ public class FFmpegCapabilities : IFFmpegCapabilities @@ -9,17 +9,20 @@ public class FFmpegCapabilities : IFFmpegCapabilities
private readonly IReadOnlySet<string> _ffmpegDecoders;
private readonly IReadOnlySet<string> _ffmpegEncoders;
private readonly IReadOnlySet<string> _ffmpegFilters;
private readonly IReadOnlySet<string> _ffmpegOptions;
public FFmpegCapabilities(
IReadOnlySet<string> ffmpegHardwareAccelerations,
IReadOnlySet<string> ffmpegDecoders,
IReadOnlySet<string> ffmpegFilters,
IReadOnlySet<string> ffmpegEncoders)
IReadOnlySet<string> ffmpegEncoders,
IReadOnlySet<string> ffmpegOptions)
{
_ffmpegHardwareAccelerations = ffmpegHardwareAccelerations;
_ffmpegDecoders = ffmpegDecoders;
_ffmpegFilters = ffmpegFilters;
_ffmpegEncoders = ffmpegEncoders;
_ffmpegOptions = ffmpegOptions;
}
public bool HasHardwareAcceleration(HardwareAccelerationMode hardwareAccelerationMode)
@ -43,6 +46,8 @@ public class FFmpegCapabilities : IFFmpegCapabilities @@ -43,6 +46,8 @@ public class FFmpegCapabilities : IFFmpegCapabilities
public bool HasFilter(string filter) => _ffmpegFilters.Contains(filter);
public bool HasOption(string ffmpegOption) => _ffmpegOptions.Contains(ffmpegOption);
public Option<IDecoder> SoftwareDecoderForVideoFormat(string videoFormat) =>
videoFormat switch
{

42
ErsatzTV.FFmpeg/Capabilities/HardwareCapabilitiesFactory.cs

@ -46,8 +46,14 @@ public class HardwareCapabilitiesFactory : IHardwareCapabilitiesFactory @@ -46,8 +46,14 @@ public class HardwareCapabilitiesFactory : IHardwareCapabilitiesFactory
IReadOnlySet<string> ffmpegDecoders = await GetFFmpegCapabilities(ffmpegPath, "decoders", ParseFFmpegLine);
IReadOnlySet<string> ffmpegFilters = await GetFFmpegCapabilities(ffmpegPath, "filters", ParseFFmpegLine);
IReadOnlySet<string> ffmpegEncoders = await GetFFmpegCapabilities(ffmpegPath, "encoders", ParseFFmpegLine);
return new FFmpegCapabilities(ffmpegHardwareAccelerations, ffmpegDecoders, ffmpegFilters, ffmpegEncoders);
IReadOnlySet<string> ffmpegOptions = await GetFFmpegOptions(ffmpegPath);
return new FFmpegCapabilities(
ffmpegHardwareAccelerations,
ffmpegDecoders,
ffmpegFilters,
ffmpegEncoders,
ffmpegOptions);
}
public async Task<IHardwareCapabilities> GetHardwareCapabilities(
@ -185,6 +191,31 @@ public class HardwareCapabilitiesFactory : IHardwareCapabilitiesFactory @@ -185,6 +191,31 @@ public class HardwareCapabilitiesFactory : IHardwareCapabilitiesFactory
.Bind(l => parseLine(l))
.ToImmutableHashSet();
}
private async Task<IReadOnlySet<string>> GetFFmpegOptions(string ffmpegPath)
{
var cacheKey = string.Format(CultureInfo.InvariantCulture, FFmpegCapabilitiesCacheKeyFormat, "options");
if (_memoryCache.TryGetValue(cacheKey, out IReadOnlySet<string>? cachedCapabilities) &&
cachedCapabilities is not null)
{
return cachedCapabilities;
}
string[] arguments = { "-hide_banner", "-h", "long" };
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 => ParseFFmpegOptionLine(l))
.ToImmutableHashSet();
}
private static Option<string> ParseFFmpegAccelLine(string input)
{
@ -200,6 +231,13 @@ public class HardwareCapabilitiesFactory : IHardwareCapabilitiesFactory @@ -200,6 +231,13 @@ public class HardwareCapabilitiesFactory : IHardwareCapabilitiesFactory
return match.Success ? match.Groups[1].Value : Option<string>.None;
}
private static Option<string> ParseFFmpegOptionLine(string input)
{
const string PATTERN = @"^-([a-z_]+)\s+.*";
Match match = Regex.Match(input, PATTERN);
return match.Success ? match.Groups[1].Value : Option<string>.None;
}
private async Task<IHardwareCapabilities> GetVaapiCapabilities(
Option<string> vaapiDriver,
Option<string> vaapiDevice)

1
ErsatzTV.FFmpeg/Capabilities/IFFmpegCapabilities.cs

@ -8,5 +8,6 @@ public interface IFFmpegCapabilities @@ -8,5 +8,6 @@ public interface IFFmpegCapabilities
bool HasDecoder(string decoder);
bool HasEncoder(string encoder);
bool HasFilter(string filter);
bool HasOption(string ffmpegOption);
Option<IDecoder> SoftwareDecoderForVideoFormat(string videoFormat);
}

22
ErsatzTV.FFmpeg/InputOption/ReadrateInputOption.cs

@ -1,17 +1,26 @@ @@ -1,17 +1,26 @@
using System.Globalization;
using ErsatzTV.FFmpeg.Capabilities;
using ErsatzTV.FFmpeg.Environment;
using Microsoft.Extensions.Logging;
namespace ErsatzTV.FFmpeg.InputOption;
public class ReadrateInputOption : IInputOption
{
private readonly IFFmpegCapabilities _ffmpegCapabilities;
private readonly int _initialBurstSeconds;
private readonly ILogger _logger;
public ReadrateInputOption(int initialBurstSeconds = 0)
public ReadrateInputOption(
IFFmpegCapabilities ffmpegCapabilities,
int initialBurstSeconds,
ILogger logger)
{
_ffmpegCapabilities = ffmpegCapabilities;
_initialBurstSeconds = initialBurstSeconds;
_logger = logger;
}
public IList<EnvironmentVariable> EnvironmentVariables => Array.Empty<EnvironmentVariable>();
public IList<string> GlobalOptions => Array.Empty<string>();
@ -22,6 +31,15 @@ public class ReadrateInputOption : IInputOption @@ -22,6 +31,15 @@ public class ReadrateInputOption : IInputOption
if (_initialBurstSeconds > 0)
{
if (!_ffmpegCapabilities.HasOption("readrate_initial_burst"))
{
_logger.LogWarning(
"FFmpeg is missing {Option} option; unable to transcode faster than realtime",
"readrate_initial_burst");
return result;
}
result.AddRange(
new[]
{

8
ErsatzTV.FFmpeg/Pipeline/PipelineBuilderBase.cs

@ -84,7 +84,7 @@ public abstract class PipelineBuilderBase : IPipelineBuilder @@ -84,7 +84,7 @@ public abstract class PipelineBuilderBase : IPipelineBuilder
};
concatInputFile.AddOption(new ConcatInputFormat());
concatInputFile.AddOption(new ReadrateInputOption());
concatInputFile.AddOption(new ReadrateInputOption(_ffmpegCapabilities, 0, _logger));
concatInputFile.AddOption(new InfiniteLoopInputOption(HardwareAccelerationMode.None));
foreach (int threadCount in ffmpegState.ThreadCount)
@ -130,7 +130,7 @@ public abstract class PipelineBuilderBase : IPipelineBuilder @@ -130,7 +130,7 @@ public abstract class PipelineBuilderBase : IPipelineBuilder
new EncoderCopyAll()
};
concatInputFile.AddOption(new ReadrateInputOption());
concatInputFile.AddOption(new ReadrateInputOption(_ffmpegCapabilities, 0, _logger));
SetMetadataServiceProvider(ffmpegState, pipelineSteps);
SetMetadataServiceName(ffmpegState, pipelineSteps);
@ -640,8 +640,8 @@ public abstract class PipelineBuilderBase : IPipelineBuilder @@ -640,8 +640,8 @@ public abstract class PipelineBuilderBase : IPipelineBuilder
};
}
_audioInputFile.Iter(a => a.AddOption(new ReadrateInputOption(initialBurst)));
videoInputFile.AddOption(new ReadrateInputOption(initialBurst));
_audioInputFile.Iter(a => a.AddOption(new ReadrateInputOption(_ffmpegCapabilities, initialBurst, _logger)));
videoInputFile.AddOption(new ReadrateInputOption(_ffmpegCapabilities, initialBurst, _logger));
}
private static void SetStillImageInfiniteLoop(

Loading…
Cancel
Save