From eca9bf41bcf536708ad74236793b363db3af1e4d Mon Sep 17 00:00:00 2001 From: nyanmisaka Date: Sat, 16 Mar 2024 03:40:14 +0800 Subject: [PATCH 01/10] Add TranscodingSegmentCleaner to replace ffmpeg's hlsenc deletion FFmpeg deletes segments based on its own transcoding progress, but we need to delete segments based on client download progress. Since disk and GPU speeds vary, using hlsenc's built-in deletion will result in premature deletion of some segments. As a consequence, the server has to constantly respin new ffmpeg instances, resulting in choppy video playback. Signed-off-by: nyanmisaka --- .../Controllers/DynamicHlsController.cs | 29 +-- .../MediaEncoding/TranscodingJob.cs | 8 + .../TranscodingSegmentCleaner.cs | 188 ++++++++++++++++++ .../Transcoding/TranscodeManager.cs | 17 ++ 4 files changed, 214 insertions(+), 28 deletions(-) create mode 100644 MediaBrowser.Controller/MediaEncoding/TranscodingSegmentCleaner.cs diff --git a/Jellyfin.Api/Controllers/DynamicHlsController.cs b/Jellyfin.Api/Controllers/DynamicHlsController.cs index 590cdc33f0..d70c51b86f 100644 --- a/Jellyfin.Api/Controllers/DynamicHlsController.cs +++ b/Jellyfin.Api/Controllers/DynamicHlsController.cs @@ -1604,7 +1604,7 @@ public class DynamicHlsController : BaseJellyfinApiController Path.GetFileNameWithoutExtension(outputPath)); } - var hlsArguments = GetHlsArguments(isEventPlaylist, state.SegmentLength); + var hlsArguments = string.Format(CultureInfo.InvariantCulture, "-hls_playlist_type {0} -hls_list_size 0", isEventPlaylist ? "event" : "vod"); return string.Format( CultureInfo.InvariantCulture, @@ -1625,33 +1625,6 @@ public class DynamicHlsController : BaseJellyfinApiController EncodingUtils.NormalizePath(outputPath)).Trim(); } - /// - /// Gets the HLS arguments for transcoding. - /// - /// The command line arguments for HLS transcoding. - private string GetHlsArguments(bool isEventPlaylist, int segmentLength) - { - var enableThrottling = _encodingOptions.EnableThrottling; - var enableSegmentDeletion = _encodingOptions.EnableSegmentDeletion; - - // Only enable segment deletion when throttling is enabled - if (enableThrottling && enableSegmentDeletion) - { - // Store enough segments for configured seconds of playback; this needs to be above throttling settings - var segmentCount = _encodingOptions.SegmentKeepSeconds / segmentLength; - - _logger.LogDebug("Using throttling and segment deletion, keeping {0} segments", segmentCount); - - return string.Format(CultureInfo.InvariantCulture, "-hls_list_size {0} -hls_flags delete_segments", segmentCount.ToString(CultureInfo.InvariantCulture)); - } - else - { - _logger.LogDebug("Using normal playback, is event playlist? {0}", isEventPlaylist); - - return string.Format(CultureInfo.InvariantCulture, "-hls_playlist_type {0} -hls_list_size 0", isEventPlaylist ? "event" : "vod"); - } - } - /// /// Gets the audio arguments for transcoding. /// diff --git a/MediaBrowser.Controller/MediaEncoding/TranscodingJob.cs b/MediaBrowser.Controller/MediaEncoding/TranscodingJob.cs index 1e6d5933c8..2b6540ea88 100644 --- a/MediaBrowser.Controller/MediaEncoding/TranscodingJob.cs +++ b/MediaBrowser.Controller/MediaEncoding/TranscodingJob.cs @@ -136,6 +136,11 @@ public sealed class TranscodingJob : IDisposable /// public TranscodingThrottler? TranscodingThrottler { get; set; } + /// + /// Gets or sets transcoding segment cleaner. + /// + public TranscodingSegmentCleaner? TranscodingSegmentCleaner { get; set; } + /// /// Gets or sets last ping date. /// @@ -239,6 +244,7 @@ public sealed class TranscodingJob : IDisposable { #pragma warning disable CA1849 // Can't await in lock block TranscodingThrottler?.Stop().GetAwaiter().GetResult(); + TranscodingSegmentCleaner?.Stop(); var process = Process; @@ -276,5 +282,7 @@ public sealed class TranscodingJob : IDisposable CancellationTokenSource = null; TranscodingThrottler?.Dispose(); TranscodingThrottler = null; + TranscodingSegmentCleaner?.Dispose(); + TranscodingSegmentCleaner = null; } } diff --git a/MediaBrowser.Controller/MediaEncoding/TranscodingSegmentCleaner.cs b/MediaBrowser.Controller/MediaEncoding/TranscodingSegmentCleaner.cs new file mode 100644 index 0000000000..6cbda8e0a7 --- /dev/null +++ b/MediaBrowser.Controller/MediaEncoding/TranscodingSegmentCleaner.cs @@ -0,0 +1,188 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using MediaBrowser.Common.Configuration; +using MediaBrowser.Model.Configuration; +using MediaBrowser.Model.IO; +using Microsoft.Extensions.Logging; + +namespace MediaBrowser.Controller.MediaEncoding; + +/// +/// Transcoding segment cleaner. +/// +public class TranscodingSegmentCleaner : IDisposable +{ + private readonly TranscodingJob _job; + private readonly ILogger _logger; + private readonly IConfigurationManager _config; + private readonly IFileSystem _fileSystem; + private readonly IMediaEncoder _mediaEncoder; + private Timer? _timer; + private int _segmentLength; + + /// + /// Initializes a new instance of the class. + /// + /// Transcoding job dto. + /// Instance of the interface. + /// Instance of the interface. + /// Instance of the interface. + /// Instance of the interface. + /// The segment length of this transcoding job. + public TranscodingSegmentCleaner(TranscodingJob job, ILogger logger, IConfigurationManager config, IFileSystem fileSystem, IMediaEncoder mediaEncoder, int segmentLength) + { + _job = job; + _logger = logger; + _config = config; + _fileSystem = fileSystem; + _mediaEncoder = mediaEncoder; + _segmentLength = segmentLength; + } + + /// + /// Start timer. + /// + public void Start() + { + _timer = new Timer(TimerCallback, null, 20000, 20000); + } + + /// + /// Stop cleaner. + /// + public void Stop() + { + DisposeTimer(); + } + + /// + /// Dispose cleaner. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// Dispose cleaner. + /// + /// Disposing. + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + DisposeTimer(); + } + } + + private EncodingOptions GetOptions() + { + return _config.GetEncodingOptions(); + } + + private async void TimerCallback(object? state) + { + if (_job.HasExited) + { + DisposeTimer(); + return; + } + + var options = GetOptions(); + var enableSegmentDeletion = options.EnableSegmentDeletion; + var segmentKeepSeconds = Math.Max(options.SegmentKeepSeconds, 20); + + if (enableSegmentDeletion) + { + var downloadPositionTicks = _job.DownloadPositionTicks ?? 0; + var downloadPositionSeconds = Convert.ToInt64(TimeSpan.FromTicks(downloadPositionTicks).TotalSeconds); + + if (downloadPositionSeconds > 0 && segmentKeepSeconds > 0 && downloadPositionSeconds > segmentKeepSeconds) + { + var idxMaxToRemove = (downloadPositionSeconds - segmentKeepSeconds) / _segmentLength; + + if (idxMaxToRemove > 0) + { + await DeleteSegmentFiles(_job, 0, idxMaxToRemove, 0, 1500).ConfigureAwait(false); + } + } + } + } + + private async Task DeleteSegmentFiles(TranscodingJob job, long idxMin, long idxMax, int retryCount, int delayMs) + { + if (retryCount >= 10) + { + return; + } + + var path = job.Path ?? throw new ArgumentException("Path can't be null."); + + _logger.LogDebug("Deleting segment file(s) index {Min} to {Max} from {Path}", idxMin, idxMax, path); + + await Task.Delay(delayMs).ConfigureAwait(false); + + try + { + if (job.Type == TranscodingJobType.Hls) + { + DeleteHlsSegmentFiles(path, idxMin, idxMax); + } + } + catch (IOException ex) + { + _logger.LogError(ex, "Error deleting segment file(s) {Path}", path); + + await DeleteSegmentFiles(job, idxMin, idxMax, retryCount + 1, 500).ConfigureAwait(false); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error deleting segment file(s) {Path}", path); + } + } + + private void DeleteHlsSegmentFiles(string outputFilePath, long idxMin, long idxMax) + { + var directory = Path.GetDirectoryName(outputFilePath) + ?? throw new ArgumentException("Path can't be a root directory.", nameof(outputFilePath)); + + var name = Path.GetFileNameWithoutExtension(outputFilePath); + + var filesToDelete = _fileSystem.GetFilePaths(directory) + .Where(f => long.TryParse(Path.GetFileNameWithoutExtension(f).Replace(name, string.Empty, StringComparison.Ordinal), out var idx) && idx >= idxMin && idx <= idxMax); + + List? exs = null; + foreach (var file in filesToDelete) + { + try + { + _logger.LogDebug("Deleting HLS segment file {0}", file); + _fileSystem.DeleteFile(file); + } + catch (IOException ex) + { + (exs ??= new List(4)).Add(ex); + _logger.LogError(ex, "Error deleting HLS segment file {Path}", file); + } + } + + if (exs is not null) + { + throw new AggregateException("Error deleting HLS segment files", exs); + } + } + + private void DisposeTimer() + { + if (_timer is not null) + { + _timer.Dispose(); + _timer = null; + } + } +} diff --git a/MediaBrowser.MediaEncoding/Transcoding/TranscodeManager.cs b/MediaBrowser.MediaEncoding/Transcoding/TranscodeManager.cs index 8bace15c65..2a72cacdc9 100644 --- a/MediaBrowser.MediaEncoding/Transcoding/TranscodeManager.cs +++ b/MediaBrowser.MediaEncoding/Transcoding/TranscodeManager.cs @@ -546,6 +546,7 @@ public sealed class TranscodeManager : ITranscodeManager, IDisposable if (!transcodingJob.HasExited) { StartThrottler(state, transcodingJob); + StartSegmentCleaner(state, transcodingJob); } else if (transcodingJob.ExitCode != 0) { @@ -573,6 +574,22 @@ public sealed class TranscodeManager : ITranscodeManager, IDisposable && state.IsInputVideo && state.VideoType == VideoType.VideoFile; + private void StartSegmentCleaner(StreamState state, TranscodingJob transcodingJob) + { + if (EnableSegmentCleaning(state)) + { + transcodingJob.TranscodingSegmentCleaner = new TranscodingSegmentCleaner(transcodingJob, _loggerFactory.CreateLogger(), _serverConfigurationManager, _fileSystem, _mediaEncoder, state.SegmentLength); + transcodingJob.TranscodingSegmentCleaner.Start(); + } + } + + private static bool EnableSegmentCleaning(StreamState state) + => state.InputProtocol is MediaProtocol.File or MediaProtocol.Http + && state.IsInputVideo + && state.TranscodingType == TranscodingJobType.Hls + && state.RunTimeTicks.HasValue + && state.RunTimeTicks.Value >= TimeSpan.FromMinutes(5).Ticks; + private TranscodingJob OnTranscodeBeginning( string path, string? playSessionId, From 55fd6b5cb90fe6fa1f10f706cdbe0a7ecdd54fbb Mon Sep 17 00:00:00 2001 From: nyanmisaka Date: Sat, 16 Mar 2024 03:41:24 +0800 Subject: [PATCH 02/10] Add sanity check for ThrottleDelaySeconds Signed-off-by: nyanmisaka --- MediaBrowser.Controller/MediaEncoding/TranscodingThrottler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Controller/MediaEncoding/TranscodingThrottler.cs b/MediaBrowser.Controller/MediaEncoding/TranscodingThrottler.cs index 813f13eaef..b95e6ed51f 100644 --- a/MediaBrowser.Controller/MediaEncoding/TranscodingThrottler.cs +++ b/MediaBrowser.Controller/MediaEncoding/TranscodingThrottler.cs @@ -115,7 +115,7 @@ public class TranscodingThrottler : IDisposable var options = GetOptions(); - if (options.EnableThrottling && IsThrottleAllowed(_job, options.ThrottleDelaySeconds)) + if (options.EnableThrottling && IsThrottleAllowed(_job, Math.Max(options.ThrottleDelaySeconds, 60))) { await PauseTranscoding().ConfigureAwait(false); } From 39b953e41caf6f0c333296af8d360b6a2559b835 Mon Sep 17 00:00:00 2001 From: nyanmisaka Date: Sat, 16 Mar 2024 03:51:54 +0800 Subject: [PATCH 03/10] Set input readrate for using SegmentDeletion with stream-copy Signed-off-by: nyanmisaka --- .../MediaEncoding/EncodingHelper.cs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 946f7266c3..5daa03935b 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -51,6 +51,8 @@ namespace MediaBrowser.Controller.MediaEncoding private readonly Version _minFFmpegOclCuTonemapMode = new Version(5, 1, 3); private readonly Version _minFFmpegSvtAv1Params = new Version(5, 1); private readonly Version _minFFmpegVaapiH26xEncA53CcSei = new Version(6, 0); + private readonly Version _minFFmpegAutoscaleOption = new Version(4, 4); + private readonly Version _minFFmpegReadrateOption = new Version(5, 0); private static readonly string[] _videoProfilesH264 = new[] { @@ -1221,7 +1223,7 @@ namespace MediaBrowser.Controller.MediaEncoding // Disable auto inserted SW scaler for HW decoders in case of changed resolution. var isSwDecoder = string.IsNullOrEmpty(GetHardwareVideoDecoder(state, options)); - if (!isSwDecoder && _mediaEncoder.EncoderVersion >= new Version(4, 4)) + if (!isSwDecoder && _mediaEncoder.EncoderVersion >= _minFFmpegAutoscaleOption) { arg.Append(" -noautoscale"); } @@ -6393,6 +6395,16 @@ namespace MediaBrowser.Controller.MediaEncoding { inputModifier += " -re"; } + else if (encodingOptions.EnableSegmentDeletion + && state.VideoStream is not null + && state.TranscodingType == TranscodingJobType.Hls + && IsCopyCodec(state.OutputVideoCodec) + && _mediaEncoder.EncoderVersion >= _minFFmpegReadrateOption) + { + // Set an input read rate limit 10x for using SegmentDeletion with stream-copy + // to prevent ffmpeg from exiting prematurely (due to fast drive) + inputModifier += " -readrate 10"; + } var flags = new List(); if (state.IgnoreInputDts) From 50541aea91629f11b1ead72b059f09c91dd758ab Mon Sep 17 00:00:00 2001 From: nyanmisaka Date: Sat, 16 Mar 2024 22:09:31 +0800 Subject: [PATCH 04/10] Apply suggestions from code review Add excludeFilePaths to skip segment files in which IOException occurred. Signed-off-by: nyanmisaka --- .../TranscodingSegmentCleaner.cs | 26 +++++++------------ 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/MediaBrowser.Controller/MediaEncoding/TranscodingSegmentCleaner.cs b/MediaBrowser.Controller/MediaEncoding/TranscodingSegmentCleaner.cs index 6cbda8e0a7..d18f26b8b2 100644 --- a/MediaBrowser.Controller/MediaEncoding/TranscodingSegmentCleaner.cs +++ b/MediaBrowser.Controller/MediaEncoding/TranscodingSegmentCleaner.cs @@ -23,6 +23,7 @@ public class TranscodingSegmentCleaner : IDisposable private readonly IMediaEncoder _mediaEncoder; private Timer? _timer; private int _segmentLength; + private List? _excludeFilePaths; /// /// Initializes a new instance of the class. @@ -41,6 +42,7 @@ public class TranscodingSegmentCleaner : IDisposable _fileSystem = fileSystem; _mediaEncoder = mediaEncoder; _segmentLength = segmentLength; + _excludeFilePaths = null; } /// @@ -104,23 +106,18 @@ public class TranscodingSegmentCleaner : IDisposable if (downloadPositionSeconds > 0 && segmentKeepSeconds > 0 && downloadPositionSeconds > segmentKeepSeconds) { - var idxMaxToRemove = (downloadPositionSeconds - segmentKeepSeconds) / _segmentLength; + var idxMaxToDelete = (downloadPositionSeconds - segmentKeepSeconds) / _segmentLength; - if (idxMaxToRemove > 0) + if (idxMaxToDelete > 0) { - await DeleteSegmentFiles(_job, 0, idxMaxToRemove, 0, 1500).ConfigureAwait(false); + await DeleteSegmentFiles(_job, 0, idxMaxToDelete, 1500).ConfigureAwait(false); } } } } - private async Task DeleteSegmentFiles(TranscodingJob job, long idxMin, long idxMax, int retryCount, int delayMs) + private async Task DeleteSegmentFiles(TranscodingJob job, long idxMin, long idxMax, int delayMs) { - if (retryCount >= 10) - { - return; - } - var path = job.Path ?? throw new ArgumentException("Path can't be null."); _logger.LogDebug("Deleting segment file(s) index {Min} to {Max} from {Path}", idxMin, idxMax, path); @@ -134,12 +131,6 @@ public class TranscodingSegmentCleaner : IDisposable DeleteHlsSegmentFiles(path, idxMin, idxMax); } } - catch (IOException ex) - { - _logger.LogError(ex, "Error deleting segment file(s) {Path}", path); - - await DeleteSegmentFiles(job, idxMin, idxMax, retryCount + 1, 500).ConfigureAwait(false); - } catch (Exception ex) { _logger.LogError(ex, "Error deleting segment file(s) {Path}", path); @@ -154,7 +145,9 @@ public class TranscodingSegmentCleaner : IDisposable var name = Path.GetFileNameWithoutExtension(outputFilePath); var filesToDelete = _fileSystem.GetFilePaths(directory) - .Where(f => long.TryParse(Path.GetFileNameWithoutExtension(f).Replace(name, string.Empty, StringComparison.Ordinal), out var idx) && idx >= idxMin && idx <= idxMax); + .Where(f => (!_excludeFilePaths?.Contains(f) ?? true) + && long.TryParse(Path.GetFileNameWithoutExtension(f).Replace(name, string.Empty, StringComparison.Ordinal), out var idx) + && (idx >= idxMin && idx <= idxMax)); List? exs = null; foreach (var file in filesToDelete) @@ -167,6 +160,7 @@ public class TranscodingSegmentCleaner : IDisposable catch (IOException ex) { (exs ??= new List(4)).Add(ex); + (_excludeFilePaths ??= new List()).Add(file); _logger.LogError(ex, "Error deleting HLS segment file {Path}", file); } } From 47a77974b8a85bff007f439d9c9291220069017a Mon Sep 17 00:00:00 2001 From: nyanmisaka Date: Sun, 17 Mar 2024 17:17:47 +0800 Subject: [PATCH 05/10] Apply suggestions from code review Drop excludeFilePaths and lower the log level to debug to avoid spamming in the log file. Signed-off-by: nyanmisaka --- .../MediaEncoding/TranscodingSegmentCleaner.cs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/MediaBrowser.Controller/MediaEncoding/TranscodingSegmentCleaner.cs b/MediaBrowser.Controller/MediaEncoding/TranscodingSegmentCleaner.cs index d18f26b8b2..a6d812873f 100644 --- a/MediaBrowser.Controller/MediaEncoding/TranscodingSegmentCleaner.cs +++ b/MediaBrowser.Controller/MediaEncoding/TranscodingSegmentCleaner.cs @@ -23,7 +23,6 @@ public class TranscodingSegmentCleaner : IDisposable private readonly IMediaEncoder _mediaEncoder; private Timer? _timer; private int _segmentLength; - private List? _excludeFilePaths; /// /// Initializes a new instance of the class. @@ -42,7 +41,6 @@ public class TranscodingSegmentCleaner : IDisposable _fileSystem = fileSystem; _mediaEncoder = mediaEncoder; _segmentLength = segmentLength; - _excludeFilePaths = null; } /// @@ -133,7 +131,7 @@ public class TranscodingSegmentCleaner : IDisposable } catch (Exception ex) { - _logger.LogError(ex, "Error deleting segment file(s) {Path}", path); + _logger.LogDebug(ex, "Error deleting segment file(s) {Path}", path); } } @@ -145,8 +143,7 @@ public class TranscodingSegmentCleaner : IDisposable var name = Path.GetFileNameWithoutExtension(outputFilePath); var filesToDelete = _fileSystem.GetFilePaths(directory) - .Where(f => (!_excludeFilePaths?.Contains(f) ?? true) - && long.TryParse(Path.GetFileNameWithoutExtension(f).Replace(name, string.Empty, StringComparison.Ordinal), out var idx) + .Where(f => long.TryParse(Path.GetFileNameWithoutExtension(f).Replace(name, string.Empty, StringComparison.Ordinal), out var idx) && (idx >= idxMin && idx <= idxMax)); List? exs = null; @@ -160,8 +157,7 @@ public class TranscodingSegmentCleaner : IDisposable catch (IOException ex) { (exs ??= new List(4)).Add(ex); - (_excludeFilePaths ??= new List()).Add(file); - _logger.LogError(ex, "Error deleting HLS segment file {Path}", file); + _logger.LogDebug(ex, "Error deleting HLS segment file {Path}", file); } } From 98debe4817d5179051242aefe135db964fd32aa2 Mon Sep 17 00:00:00 2001 From: Nyanmisaka Date: Sun, 17 Mar 2024 20:34:18 +0800 Subject: [PATCH 06/10] Apply suggestions from code review String interpolation is preferred. Co-authored-by: Claus Vium --- Jellyfin.Api/Controllers/DynamicHlsController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jellyfin.Api/Controllers/DynamicHlsController.cs b/Jellyfin.Api/Controllers/DynamicHlsController.cs index d70c51b86f..4aa9725744 100644 --- a/Jellyfin.Api/Controllers/DynamicHlsController.cs +++ b/Jellyfin.Api/Controllers/DynamicHlsController.cs @@ -1604,7 +1604,7 @@ public class DynamicHlsController : BaseJellyfinApiController Path.GetFileNameWithoutExtension(outputPath)); } - var hlsArguments = string.Format(CultureInfo.InvariantCulture, "-hls_playlist_type {0} -hls_list_size 0", isEventPlaylist ? "event" : "vod"); + var hlsArguments = $"-hls_playlist_type {(isEventPlaylist ? "event" : "vod")} -hls_list_size 0"; return string.Format( CultureInfo.InvariantCulture, From a3ba974b7bfa3899434ba65221a5b6750d7022bf Mon Sep 17 00:00:00 2001 From: nyanmisaka Date: Sun, 17 Mar 2024 20:44:42 +0800 Subject: [PATCH 07/10] Fix the trailing whitespace Signed-off-by: nyanmisaka --- Jellyfin.Api/Controllers/DynamicHlsController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jellyfin.Api/Controllers/DynamicHlsController.cs b/Jellyfin.Api/Controllers/DynamicHlsController.cs index 4aa9725744..1bdda8ad2f 100644 --- a/Jellyfin.Api/Controllers/DynamicHlsController.cs +++ b/Jellyfin.Api/Controllers/DynamicHlsController.cs @@ -1604,7 +1604,7 @@ public class DynamicHlsController : BaseJellyfinApiController Path.GetFileNameWithoutExtension(outputPath)); } - var hlsArguments = $"-hls_playlist_type {(isEventPlaylist ? "event" : "vod")} -hls_list_size 0"; + var hlsArguments = $"-hls_playlist_type {(isEventPlaylist ? "event" : "vod")} -hls_list_size 0"; return string.Format( CultureInfo.InvariantCulture, From 557b8f0c7879261d2dc8268e77836fd6efb7feb8 Mon Sep 17 00:00:00 2001 From: nyanmisaka Date: Sun, 17 Mar 2024 20:45:00 +0800 Subject: [PATCH 08/10] Apply suggestions from code review Drop the unnecessary initial capacity from the list. Signed-off-by: nyanmisaka --- .../MediaEncoding/TranscodingSegmentCleaner.cs | 2 +- MediaBrowser.MediaEncoding/Transcoding/TranscodeManager.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/MediaBrowser.Controller/MediaEncoding/TranscodingSegmentCleaner.cs b/MediaBrowser.Controller/MediaEncoding/TranscodingSegmentCleaner.cs index a6d812873f..67bfcb02fd 100644 --- a/MediaBrowser.Controller/MediaEncoding/TranscodingSegmentCleaner.cs +++ b/MediaBrowser.Controller/MediaEncoding/TranscodingSegmentCleaner.cs @@ -156,7 +156,7 @@ public class TranscodingSegmentCleaner : IDisposable } catch (IOException ex) { - (exs ??= new List(4)).Add(ex); + (exs ??= new List()).Add(ex); _logger.LogDebug(ex, "Error deleting HLS segment file {Path}", file); } } diff --git a/MediaBrowser.MediaEncoding/Transcoding/TranscodeManager.cs b/MediaBrowser.MediaEncoding/Transcoding/TranscodeManager.cs index 2a72cacdc9..499e5287a7 100644 --- a/MediaBrowser.MediaEncoding/Transcoding/TranscodeManager.cs +++ b/MediaBrowser.MediaEncoding/Transcoding/TranscodeManager.cs @@ -321,7 +321,7 @@ public sealed class TranscodeManager : ITranscodeManager, IDisposable } catch (IOException ex) { - (exs ??= new List(4)).Add(ex); + (exs ??= new List()).Add(ex); _logger.LogError(ex, "Error deleting HLS file {Path}", file); } } From ae7c0c83e9f7b5e35ac067f09a92d10d33407219 Mon Sep 17 00:00:00 2001 From: nyanmisaka Date: Sun, 17 Mar 2024 21:30:42 +0800 Subject: [PATCH 09/10] Bump the required minimum ffmpeg version to 4.4 Signed-off-by: nyanmisaka --- .../MediaEncoding/EncodingHelper.cs | 3 +-- .../Encoder/EncoderValidator.cs | 21 ++++++++++--------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 5daa03935b..cdaa6a6cd2 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -51,7 +51,6 @@ namespace MediaBrowser.Controller.MediaEncoding private readonly Version _minFFmpegOclCuTonemapMode = new Version(5, 1, 3); private readonly Version _minFFmpegSvtAv1Params = new Version(5, 1); private readonly Version _minFFmpegVaapiH26xEncA53CcSei = new Version(6, 0); - private readonly Version _minFFmpegAutoscaleOption = new Version(4, 4); private readonly Version _minFFmpegReadrateOption = new Version(5, 0); private static readonly string[] _videoProfilesH264 = new[] @@ -1223,7 +1222,7 @@ namespace MediaBrowser.Controller.MediaEncoding // Disable auto inserted SW scaler for HW decoders in case of changed resolution. var isSwDecoder = string.IsNullOrEmpty(GetHardwareVideoDecoder(state, options)); - if (!isSwDecoder && _mediaEncoder.EncoderVersion >= _minFFmpegAutoscaleOption) + if (!isSwDecoder) { arg.Append(" -noautoscale"); } diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs index 6549125d36..6579f1abe5 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs @@ -146,17 +146,18 @@ namespace MediaBrowser.MediaEncoding.Encoder { 5, new string[] { "overlay_vulkan", "Action to take when encountering EOF from secondary input" } } }; - // These are the library versions that corresponds to our minimum ffmpeg version 4.x according to the version table below + // These are the library versions that corresponds to our minimum ffmpeg version 4.4 according to the version table below + // Refers to the versions in https://ffmpeg.org/download.html private static readonly Dictionary _ffmpegMinimumLibraryVersions = new Dictionary { - { "libavutil", new Version(56, 14) }, - { "libavcodec", new Version(58, 18) }, - { "libavformat", new Version(58, 12) }, - { "libavdevice", new Version(58, 3) }, - { "libavfilter", new Version(7, 16) }, - { "libswscale", new Version(5, 1) }, - { "libswresample", new Version(3, 1) }, - { "libpostproc", new Version(55, 1) } + { "libavutil", new Version(56, 70) }, + { "libavcodec", new Version(58, 134) }, + { "libavformat", new Version(58, 76) }, + { "libavdevice", new Version(58, 13) }, + { "libavfilter", new Version(7, 110) }, + { "libswscale", new Version(5, 9) }, + { "libswresample", new Version(3, 9) }, + { "libpostproc", new Version(55, 9) } }; private readonly ILogger _logger; @@ -176,7 +177,7 @@ namespace MediaBrowser.MediaEncoding.Encoder } // When changing this, also change the minimum library versions in _ffmpegMinimumLibraryVersions - public static Version MinVersion { get; } = new Version(4, 0); + public static Version MinVersion { get; } = new Version(4, 4); public static Version? MaxVersion { get; } = null; From 9b35b4e8f28e95fbd583f12e3192da3e8132dae2 Mon Sep 17 00:00:00 2001 From: nyanmisaka Date: Sun, 17 Mar 2024 22:08:05 +0800 Subject: [PATCH 10/10] Clean the outdated ffmpeg test data and add 6.1.1 Signed-off-by: nyanmisaka --- .../EncoderValidatorTests.cs | 18 +-- .../EncoderValidatorTestsData.cs | 107 ++++-------------- 2 files changed, 27 insertions(+), 98 deletions(-) diff --git a/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs b/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs index db7e91c6a2..e0a7fa3aa7 100644 --- a/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs +++ b/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs @@ -17,16 +17,11 @@ namespace Jellyfin.MediaEncoding.Tests } [Theory] + [InlineData(EncoderValidatorTestsData.FFmpegV611Output, true)] [InlineData(EncoderValidatorTestsData.FFmpegV60Output, true)] [InlineData(EncoderValidatorTestsData.FFmpegV512Output, true)] [InlineData(EncoderValidatorTestsData.FFmpegV44Output, true)] - [InlineData(EncoderValidatorTestsData.FFmpegV432Output, true)] - [InlineData(EncoderValidatorTestsData.FFmpegV431Output, true)] - [InlineData(EncoderValidatorTestsData.FFmpegV43Output, true)] - [InlineData(EncoderValidatorTestsData.FFmpegV421Output, true)] - [InlineData(EncoderValidatorTestsData.FFmpegV42Output, true)] - [InlineData(EncoderValidatorTestsData.FFmpegV414Output, true)] - [InlineData(EncoderValidatorTestsData.FFmpegV404Output, true)] + [InlineData(EncoderValidatorTestsData.FFmpegV432Output, false)] [InlineData(EncoderValidatorTestsData.FFmpegGitUnknownOutput2, true)] [InlineData(EncoderValidatorTestsData.FFmpegGitUnknownOutput, false)] public void ValidateVersionInternalTest(string versionOutput, bool valid) @@ -38,17 +33,12 @@ namespace Jellyfin.MediaEncoding.Tests { public GetFFmpegVersionTestData() { + Add(EncoderValidatorTestsData.FFmpegV611Output, new Version(6, 1, 1)); Add(EncoderValidatorTestsData.FFmpegV60Output, new Version(6, 0)); Add(EncoderValidatorTestsData.FFmpegV512Output, new Version(5, 1, 2)); Add(EncoderValidatorTestsData.FFmpegV44Output, new Version(4, 4)); Add(EncoderValidatorTestsData.FFmpegV432Output, new Version(4, 3, 2)); - Add(EncoderValidatorTestsData.FFmpegV431Output, new Version(4, 3, 1)); - Add(EncoderValidatorTestsData.FFmpegV43Output, new Version(4, 3)); - Add(EncoderValidatorTestsData.FFmpegV421Output, new Version(4, 2, 1)); - Add(EncoderValidatorTestsData.FFmpegV42Output, new Version(4, 2)); - Add(EncoderValidatorTestsData.FFmpegV414Output, new Version(4, 1, 4)); - Add(EncoderValidatorTestsData.FFmpegV404Output, new Version(4, 0, 4)); - Add(EncoderValidatorTestsData.FFmpegGitUnknownOutput2, new Version(4, 0)); + Add(EncoderValidatorTestsData.FFmpegGitUnknownOutput2, new Version(4, 4)); Add(EncoderValidatorTestsData.FFmpegGitUnknownOutput, null); } } diff --git a/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTestsData.cs b/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTestsData.cs index 89ba42da0c..30df949505 100644 --- a/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTestsData.cs +++ b/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTestsData.cs @@ -2,6 +2,18 @@ namespace Jellyfin.MediaEncoding.Tests { internal static class EncoderValidatorTestsData { + public const string FFmpegV611Output = @"ffmpeg version n6.1.1-16-g33efa50fa4-20240317 Copyright (c) 2000-2023 the FFmpeg developers +built with gcc 13.2.0 (crosstool-NG 1.26.0.65_ecc5e41) +configuration: --prefix=/ffbuild/prefix --pkg-config-flags=--static --pkg-config=pkg-config --cross-prefix=x86_64-w64-mingw32- --arch=x86_64 --target-os=mingw32 --enable-gpl --enable-version3 --disable-debug --enable-shared --disable-static --disable-w32threads --enable-pthreads --enable-iconv --enable-libxml2 --enable-zlib --enable-libfreetype --enable-libfribidi --enable-gmp --enable-lzma --enable-fontconfig --enable-libharfbuzz --enable-libvorbis --enable-opencl --disable-libpulse --enable-libvmaf --disable-libxcb --disable-xlib --enable-amf --enable-libaom --enable-libaribb24 --enable-avisynth --enable-chromaprint --enable-libdav1d --enable-libdavs2 --disable-libfdk-aac --enable-ffnvcodec --enable-cuda-llvm --enable-frei0r --enable-libgme --enable-libkvazaar --enable-libaribcaption --enable-libass --enable-libbluray --enable-libjxl --enable-libmp3lame --enable-libopus --enable-librist --enable-libssh --enable-libtheora --enable-libvpx --enable-libwebp --enable-lv2 --enable-libvpl --enable-openal --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenh264 --enable-libopenjpeg --enable-libopenmpt --enable-librav1e --enable-librubberband --enable-schannel --enable-sdl2 --enable-libsoxr --enable-libsrt --enable-libsvtav1 --enable-libtwolame --enable-libuavs3d --disable-libdrm --enable-vaapi --enable-libvidstab --enable-vulkan --enable-libshaderc --enable-libplacebo --enable-libx264 --enable-libx265 --enable-libxavs2 --enable-libxvid --enable-libzimg --enable-libzvbi --extra-cflags='$FF_CFLAGS' --extra-cxxflags='$FF_CXXFLAGS' --extra-ldflags='$FF_LDFLAGS' --extra-ldexeflags='$FF_LDEXEFLAGS' --extra-libs='$FF_LIBS' --extra-version=20240317 +libavutil 58. 29.100 / 58. 29.100 +libavcodec 60. 31.102 / 60. 31.102 +libavformat 60. 16.100 / 60. 16.100 +libavdevice 60. 3.100 / 60. 3.100 +libavfilter 9. 12.100 / 9. 12.100 +libswscale 7. 5.100 / 7. 5.100 +libswresample 4. 12.100 / 4. 12.100 +libpostproc 57. 3.100 / 57. 3.100"; + public const string FFmpegV60Output = @"ffmpeg version 6.0-Jellyfin Copyright (c) 2000-2023 the FFmpeg developers built with gcc 12.2.0 (crosstool-NG 1.25.0.90_cf9beb1) configuration: --prefix=/ffbuild/prefix --pkg-config=pkg-config --pkg-config-flags=--static --cross-prefix=x86_64-w64-mingw32- --arch=x86_64 --target-os=mingw32 --extra-version=Jellyfin --extra-cflags= --extra-cxxflags= --extra-ldflags= --extra-ldexeflags= --extra-libs= --enable-gpl --enable-version3 --enable-lto --disable-ffplay --disable-debug --disable-doc --disable-ptx-compression --disable-sdl2 --disable-w32threads --enable-pthreads --enable-iconv --enable-libxml2 --enable-zlib --enable-libfreetype --enable-libfribidi --enable-gmp --enable-lzma --enable-fontconfig --enable-libvorbis --enable-opencl --enable-amf --enable-chromaprint --enable-libdav1d --enable-dxva2 --enable-d3d11va --enable-libfdk-aac --enable-ffnvcodec --enable-cuda --enable-cuda-llvm --enable-cuvid --enable-nvdec --enable-nvenc --enable-libass --enable-libbluray --enable-libmp3lame --enable-libopus --enable-libtheora --enable-libvpx --enable-libwebp --enable-libvpl --enable-schannel --enable-libsrt --enable-libsvtav1 --enable-vulkan --enable-libshaderc --enable-libplacebo --enable-libx264 --enable-libx265 --enable-libzimg --enable-libzvbi @@ -50,90 +62,17 @@ libswscale 5. 7.100 / 5. 7.100 libswresample 3. 7.100 / 3. 7.100 libpostproc 55. 7.100 / 55. 7.100"; - public const string FFmpegV431Output = @"ffmpeg version n4.3.1 Copyright (c) 2000-2020 the FFmpeg developers -built with gcc 10.1.0 (GCC) -configuration: --prefix=/usr --disable-debug --disable-static --disable-stripping --enable-avisynth --enable-fontconfig --enable-gmp --enable-gnutls --enable-gpl --enable-ladspa --enable-libaom --enable-libass --enable-libbluray --enable-libdav1d --enable-libdrm --enable-libfreetype --enable-libfribidi --enable-libgsm --enable-libiec61883 --enable-libjack --enable-libmfx --enable-libmodplug --enable-libmp3lame --enable-libopencore_amrnb --enable-libopencore_amrwb --enable-libopenjpeg --enable-libopus --enable-libpulse --enable-librav1e --enable-libsoxr --enable-libspeex --enable-libsrt --enable-libssh --enable-libtheora --enable-libv4l2 --enable-libvidstab --enable-libvmaf --enable-libvorbis --enable-libvpx --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxcb --enable-libxml2 --enable-libxvid --enable-nvdec --enable-nvenc --enable-omx --enable-shared --enable-version3 -libavutil 56. 51.100 / 56. 51.100 -libavcodec 58. 91.100 / 58. 91.100 -libavformat 58. 45.100 / 58. 45.100 -libavdevice 58. 10.100 / 58. 10.100 -libavfilter 7. 85.100 / 7. 85.100 -libswscale 5. 7.100 / 5. 7.100 -libswresample 3. 7.100 / 3. 7.100 -libpostproc 55. 7.100 / 55. 7.100"; - - public const string FFmpegV43Output = @"ffmpeg version 4.3 Copyright (c) 2000-2020 the FFmpeg developers -built with gcc 7 (Ubuntu 7.5.0-3ubuntu1~18.04) -configuration: --prefix=/usr/lib/jellyfin-ffmpeg --target-os=linux --disable-doc --disable-ffplay --disable-shared --disable-libxcb --disable-vdpau --disable-sdl2 --disable-xlib --enable-gpl --enable-version3 --enable-static --enable-libfontconfig --enable-fontconfig --enable-gmp --enable-gnutls --enable-libass --enable-libbluray --enable-libdrm --enable-libfreetype --enable-libfribidi --enable-libmp3lame --enable-libopus --enable-libtheora --enable-libvorbis --enable-libwebp --enable-libx264 --enable-libx265 --enable-libzvbi --arch=amd64 --enable-amf --enable-nvenc --enable-nvdec --enable-vaapi --enable-opencl -libavutil 56. 51.100 / 56. 51.100 -libavcodec 58. 91.100 / 58. 91.100 -libavformat 58. 45.100 / 58. 45.100 -libavdevice 58. 10.100 / 58. 10.100 -libavfilter 7. 85.100 / 7. 85.100 -libswscale 5. 7.100 / 5. 7.100 -libswresample 3. 7.100 / 3. 7.100 -libpostproc 55. 7.100 / 55. 7.100"; - - public const string FFmpegV421Output = @"ffmpeg version 4.2.1 Copyright (c) 2000-2019 the FFmpeg developers -built with gcc 9.1.1 (GCC) 20190807 -configuration: --enable-gpl --enable-version3 --enable-sdl2 --enable-fontconfig --enable-gnutls --enable-iconv --enable-libass --enable-libdav1d --enable-libbluray --enable-libfreetype --enable-libmp3lame --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --enable-libopus --enable-libshine --enable-libsnappy --enable-libsoxr --enable-libtheora --enable-libtwolame --enable-libvpx --enable-libwavpack --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxml2 --enable-libzimg --enable-lzma --enable-zlib --enable-gmp --enable-libvidstab --enable-libvorbis --enable-libvo-amrwbenc --enable-libmysofa --enable-libspeex --enable-libxvid --enable-libaom --enable-libmfx --enable-amf --enable-ffnvcodec --enable-cuvid --enable-d3d11va --enable-nvenc --enable-nvdec --enable-dxva2 --enable-avisynth --enable-libopenmpt -libavutil 56. 31.100 / 56. 31.100 -libavcodec 58. 54.100 / 58. 54.100 -libavformat 58. 29.100 / 58. 29.100 -libavdevice 58. 8.100 / 58. 8.100 -libavfilter 7. 57.100 / 7. 57.100 -libswscale 5. 5.100 / 5. 5.100 -libswresample 3. 5.100 / 3. 5.100 -libpostproc 55. 5.100 / 55. 5.100"; - - public const string FFmpegV42Output = @"ffmpeg version n4.2 Copyright (c) 2000-2019 the FFmpeg developers -built with gcc 9.1.0 (GCC) -configuration: --prefix=/usr --disable-debug --disable-static --disable-stripping --enable-fontconfig --enable-gmp --enable-gnutls --enable-gpl --enable-ladspa --enable-libaom --enable-libass --enable-libbluray --enable-libdav1d --enable-libdrm --enable-libfreetype --enable-libfribidi --enable-libgsm --enable-libiec61883 --enable-libjack --enable-libmodplug --enable-libmp3lame --enable-libopencore_amrnb --enable-libopencore_amrwb --enable-libopenjpeg --enable-libopus --enable-libpulse --enable-libsoxr --enable-libspeex --enable-libssh --enable-libtheora --enable-libv4l2 --enable-libvidstab --enable-libvorbis --enable-libvpx --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxcb --enable-libxml2 --enable-libxvid --enable-nvdec --enable-nvenc --enable-omx --enable-shared --enable-version3 -libavutil 56. 31.100 / 56. 31.100 -libavcodec 58. 54.100 / 58. 54.100 -libavformat 58. 29.100 / 58. 29.100 -libavdevice 58. 8.100 / 58. 8.100 -libavfilter 7. 57.100 / 7. 57.100 -libswscale 5. 5.100 / 5. 5.100 -libswresample 3. 5.100 / 3. 5.100 -libpostproc 55. 5.100 / 55. 5.100"; - - public const string FFmpegV414Output = @"ffmpeg version 4.1.4-1~deb10u1 Copyright (c) 2000-2019 the FFmpeg developers -built with gcc 8 (Raspbian 8.3.0-6+rpi1) -configuration: --prefix=/usr --extra-version='1~deb10u1' --toolchain=hardened --libdir=/usr/lib/arm-linux-gnueabihf --incdir=/usr/include/arm-linux-gnueabihf --arch=arm --enable-gpl --disable-stripping --enable-avresample --disable-filter=resample --enable-avisynth --enable-gnutls --enable-ladspa --enable-libaom --enable-libass --enable-libbluray --enable-libbs2b --enable-libcaca --enable-libcdio --enable-libcodec2 --enable-libflite --enable-libfontconfig --enable-libfreetype --enable-libfribidi --enable-libgme --enable-libgsm --enable-libjack --enable-libmp3lame --enable-libmysofa --enable-libopenjpeg --enable-libopenmpt --enable-libopus --enable-libpulse --enable-librsvg --enable-librubberband --enable-libshine --enable-libsnappy --enable-libsoxr --enable-libspeex --enable-libssh --enable-libtheora --enable-libtwolame --enable-libvidstab --enable-libvorbis --enable-libvpx --enable-libwavpack --enable-libwebp --enable-libx265 --enable-libxml2 --enable-libxvid --enable-libzmq --enable-libzvbi --enable-lv2 --enable-omx --enable-openal --enable-opengl --enable-sdl2 --enable-libdc1394 --enable-libdrm --enable-libiec61883 --enable-chromaprint --enable-frei0r --enable-libx264 --enable-shared -libavutil 56. 22.100 / 56. 22.100 -libavcodec 58. 35.100 / 58. 35.100 -libavformat 58. 20.100 / 58. 20.100 -libavdevice 58. 5.100 / 58. 5.100 -libavfilter 7. 40.101 / 7. 40.101 -libavresample 4. 0. 0 / 4. 0. 0 -libswscale 5. 3.100 / 5. 3.100 -libswresample 3. 3.100 / 3. 3.100 -libpostproc 55. 3.100 / 55. 3.100"; - - public const string FFmpegV404Output = @"ffmpeg version 4.0.4 Copyright (c) 2000-2019 the FFmpeg developers -built with gcc 8 (Debian 8.3.0-6) -configuration: --toolchain=hardened --prefix=/usr --target-os=linux --enable-cross-compile --extra-cflags=--static --enable-gpl --enable-static --disable-doc --disable-ffplay --disable-shared --disable-libxcb --disable-sdl2 --disable-xlib --enable-libfontconfig --enable-fontconfig --enable-gmp --enable-gnutls --enable-libass --enable-libbluray --enable-libdrm --enable-libfreetype --enable-libfribidi --enable-libmp3lame --enable-libopus --enable-libtheora --enable-libvorbis --enable-libwebp --enable-libx264 --enable-libx265 --enable-libzvbi --enable-omx --enable-omx-rpi --enable-version3 --enable-vaapi --enable-vdpau --arch=amd64 --enable-nvenc --enable-nvdec -libavutil 56. 14.100 / 56. 14.100 -libavcodec 58. 18.100 / 58. 18.100 -libavformat 58. 12.100 / 58. 12.100 -libavdevice 58. 3.100 / 58. 3.100 -libavfilter 7. 16.100 / 7. 16.100 -libswscale 5. 1.100 / 5. 1.100 -libswresample 3. 1.100 / 3. 1.100 -libpostproc 55. 1.100 / 55. 1.100"; - - public const string FFmpegGitUnknownOutput2 = @"ffmpeg version N-94303-g7cb4f8c962 Copyright (c) 2000-2019 the FFmpeg developers -built with gcc 9.1.1 (GCC) 20190716 -configuration: --enable-gpl --enable-version3 --enable-sdl2 --enable-fontconfig --enable-gnutls --enable-iconv --enable-libass --enable-libdav1d --enable-libbluray --enable-libfreetype --enable-libmp3lame --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --enable-libopus --enable-libshine --enable-libsnappy --enable-libsoxr --enable-libtheora --enable-libtwolame --enable-libvpx --enable-libwavpack --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxml2 --enable-libzimg --enable-lzma --enable-zlib --enable-gmp --enable-libvidstab --enable-libvorbis --enable-libvo-amrwbenc --enable-libmysofa --enable-libspeex --enable-libxvid --enable-libaom --enable-libmfx --enable-amf --enable-ffnvcodec --enable-cuvid --enable-d3d11va --enable-nvenc --enable-nvdec --enable-dxva2 --enable-avisynth --enable-libopenmpt -libavutil 56. 30.100 / 56. 30.100 -libavcodec 58. 53.101 / 58. 53.101 -libavformat 58. 28.102 / 58. 28.102 -libavdevice 58. 7.100 / 58. 7.100 -libavfilter 7. 56.101 / 7. 56.101 -libswscale 5. 4.101 / 5. 4.101 -libswresample 3. 4.100 / 3. 4.100 -libpostproc 55. 4.100 / 55. 4.100"; + public const string FFmpegGitUnknownOutput2 = @"ffmpeg version N-g01fc3034ee-20240317 Copyright (c) 2000-2023 the FFmpeg developers +built with gcc 13.2.0 (crosstool-NG 1.26.0.65_ecc5e41) +configuration: --prefix=/ffbuild/prefix --pkg-config-flags=--static --pkg-config=pkg-config --cross-prefix=x86_64-w64-mingw32- --arch=x86_64 --target-os=mingw32 --enable-gpl --enable-version3 --disable-debug --enable-shared --disable-static --disable-w32threads --enable-pthreads --enable-iconv --enable-libxml2 --enable-zlib --enable-libfreetype --enable-libfribidi --enable-gmp --enable-lzma --enable-fontconfig --enable-libvorbis --enable-opencl --disable-libpulse --disable-libxcb --disable-xlib --enable-amf --enable-libaom --enable-libaribb24 --enable-avisynth --disable-chromaprint --enable-libdav1d --enable-libdavs2 --disable-libfdk-aac --enable-ffnvcodec --enable-cuda-llvm --disable-frei0r --enable-libgme --enable-libkvazaar --enable-libass --enable-libbluray --enable-libmp3lame --enable-libopus --enable-librist --enable-libssh --enable-libtheora --enable-libvpx --enable-libwebp --enable-lv2 --disable-openal --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenh264 --enable-libopenjpeg --enable-libopenmpt --enable-librav1e --enable-librubberband --enable-schannel --enable-sdl2 --enable-libsoxr --enable-libsrt --enable-libsvtav1 --enable-libtwolame --enable-libuavs3d --disable-libdrm --disable-vaapi --enable-libvidstab --disable-vulkan --enable-libx264 --enable-libx265 --enable-libxavs2 --enable-libxvid --enable-libzimg --enable-libzvbi --extra-cflags='$FF_CFLAGS' --extra-cxxflags='$FF_CXXFLAGS' --extra-ldflags='$FF_LDFLAGS' --extra-ldexeflags='$FF_LDEXEFLAGS' --extra-libs='$FF_LIBS' --extra-version=20240317 +libavutil 56. 70.100 / 56. 70.100 +libavcodec 58.134.100 / 58.134.100 +libavformat 58. 76.100 / 58. 76.100 +libavdevice 58. 13.100 / 58. 13.100 +libavfilter 7.110.100 / 7.110.100 +libswscale 5. 9.100 / 5. 9.100 +libswresample 3. 9.100 / 3. 9.100 +libpostproc 55. 9.100 / 55. 9.100"; public const string FFmpegGitUnknownOutput = @"ffmpeg version N-45325-gb173e0353-static https://johnvansickle.com/ffmpeg/ Copyright (c) 2000-2018 the FFmpeg developers built with gcc 6.3.0 (Debian 6.3.0-18+deb9u1) 20170516