mirror of https://github.com/jellyfin/jellyfin.git
Merge pull request #11161 from nyanmisaka/fix-segment-deletion
Co-authored-by: Claus Vium <cvium@users.noreply.github.com>
This commit is contained in:
commit
833bc06eb4
|
@ -1604,7 +1604,7 @@ public class DynamicHlsController : BaseJellyfinApiController
|
||||||
Path.GetFileNameWithoutExtension(outputPath));
|
Path.GetFileNameWithoutExtension(outputPath));
|
||||||
}
|
}
|
||||||
|
|
||||||
var hlsArguments = GetHlsArguments(isEventPlaylist, state.SegmentLength);
|
var hlsArguments = $"-hls_playlist_type {(isEventPlaylist ? "event" : "vod")} -hls_list_size 0";
|
||||||
|
|
||||||
return string.Format(
|
return string.Format(
|
||||||
CultureInfo.InvariantCulture,
|
CultureInfo.InvariantCulture,
|
||||||
|
@ -1625,33 +1625,6 @@ public class DynamicHlsController : BaseJellyfinApiController
|
||||||
EncodingUtils.NormalizePath(outputPath)).Trim();
|
EncodingUtils.NormalizePath(outputPath)).Trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the HLS arguments for transcoding.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>The command line arguments for HLS transcoding.</returns>
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the audio arguments for transcoding.
|
/// Gets the audio arguments for transcoding.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -51,6 +51,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||||
private readonly Version _minFFmpegOclCuTonemapMode = new Version(5, 1, 3);
|
private readonly Version _minFFmpegOclCuTonemapMode = new Version(5, 1, 3);
|
||||||
private readonly Version _minFFmpegSvtAv1Params = new Version(5, 1);
|
private readonly Version _minFFmpegSvtAv1Params = new Version(5, 1);
|
||||||
private readonly Version _minFFmpegVaapiH26xEncA53CcSei = new Version(6, 0);
|
private readonly Version _minFFmpegVaapiH26xEncA53CcSei = new Version(6, 0);
|
||||||
|
private readonly Version _minFFmpegReadrateOption = new Version(5, 0);
|
||||||
|
|
||||||
private static readonly string[] _videoProfilesH264 = new[]
|
private static readonly string[] _videoProfilesH264 = new[]
|
||||||
{
|
{
|
||||||
|
@ -1221,7 +1222,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||||
|
|
||||||
// Disable auto inserted SW scaler for HW decoders in case of changed resolution.
|
// Disable auto inserted SW scaler for HW decoders in case of changed resolution.
|
||||||
var isSwDecoder = string.IsNullOrEmpty(GetHardwareVideoDecoder(state, options));
|
var isSwDecoder = string.IsNullOrEmpty(GetHardwareVideoDecoder(state, options));
|
||||||
if (!isSwDecoder && _mediaEncoder.EncoderVersion >= new Version(4, 4))
|
if (!isSwDecoder)
|
||||||
{
|
{
|
||||||
arg.Append(" -noautoscale");
|
arg.Append(" -noautoscale");
|
||||||
}
|
}
|
||||||
|
@ -6393,6 +6394,16 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||||
{
|
{
|
||||||
inputModifier += " -re";
|
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<string>();
|
var flags = new List<string>();
|
||||||
if (state.IgnoreInputDts)
|
if (state.IgnoreInputDts)
|
||||||
|
|
|
@ -136,6 +136,11 @@ public sealed class TranscodingJob : IDisposable
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public TranscodingThrottler? TranscodingThrottler { get; set; }
|
public TranscodingThrottler? TranscodingThrottler { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets transcoding segment cleaner.
|
||||||
|
/// </summary>
|
||||||
|
public TranscodingSegmentCleaner? TranscodingSegmentCleaner { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets last ping date.
|
/// Gets or sets last ping date.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -239,6 +244,7 @@ public sealed class TranscodingJob : IDisposable
|
||||||
{
|
{
|
||||||
#pragma warning disable CA1849 // Can't await in lock block
|
#pragma warning disable CA1849 // Can't await in lock block
|
||||||
TranscodingThrottler?.Stop().GetAwaiter().GetResult();
|
TranscodingThrottler?.Stop().GetAwaiter().GetResult();
|
||||||
|
TranscodingSegmentCleaner?.Stop();
|
||||||
|
|
||||||
var process = Process;
|
var process = Process;
|
||||||
|
|
||||||
|
@ -276,5 +282,7 @@ public sealed class TranscodingJob : IDisposable
|
||||||
CancellationTokenSource = null;
|
CancellationTokenSource = null;
|
||||||
TranscodingThrottler?.Dispose();
|
TranscodingThrottler?.Dispose();
|
||||||
TranscodingThrottler = null;
|
TranscodingThrottler = null;
|
||||||
|
TranscodingSegmentCleaner?.Dispose();
|
||||||
|
TranscodingSegmentCleaner = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,178 @@
|
||||||
|
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;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Transcoding segment cleaner.
|
||||||
|
/// </summary>
|
||||||
|
public class TranscodingSegmentCleaner : IDisposable
|
||||||
|
{
|
||||||
|
private readonly TranscodingJob _job;
|
||||||
|
private readonly ILogger<TranscodingSegmentCleaner> _logger;
|
||||||
|
private readonly IConfigurationManager _config;
|
||||||
|
private readonly IFileSystem _fileSystem;
|
||||||
|
private readonly IMediaEncoder _mediaEncoder;
|
||||||
|
private Timer? _timer;
|
||||||
|
private int _segmentLength;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="TranscodingSegmentCleaner"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="job">Transcoding job dto.</param>
|
||||||
|
/// <param name="logger">Instance of the <see cref="ILogger{TranscodingSegmentCleaner}"/> interface.</param>
|
||||||
|
/// <param name="config">Instance of the <see cref="IConfigurationManager"/> interface.</param>
|
||||||
|
/// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param>
|
||||||
|
/// <param name="mediaEncoder">Instance of the <see cref="IMediaEncoder"/> interface.</param>
|
||||||
|
/// <param name="segmentLength">The segment length of this transcoding job.</param>
|
||||||
|
public TranscodingSegmentCleaner(TranscodingJob job, ILogger<TranscodingSegmentCleaner> logger, IConfigurationManager config, IFileSystem fileSystem, IMediaEncoder mediaEncoder, int segmentLength)
|
||||||
|
{
|
||||||
|
_job = job;
|
||||||
|
_logger = logger;
|
||||||
|
_config = config;
|
||||||
|
_fileSystem = fileSystem;
|
||||||
|
_mediaEncoder = mediaEncoder;
|
||||||
|
_segmentLength = segmentLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Start timer.
|
||||||
|
/// </summary>
|
||||||
|
public void Start()
|
||||||
|
{
|
||||||
|
_timer = new Timer(TimerCallback, null, 20000, 20000);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Stop cleaner.
|
||||||
|
/// </summary>
|
||||||
|
public void Stop()
|
||||||
|
{
|
||||||
|
DisposeTimer();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Dispose cleaner.
|
||||||
|
/// </summary>
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Dispose(true);
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Dispose cleaner.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="disposing">Disposing.</param>
|
||||||
|
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 idxMaxToDelete = (downloadPositionSeconds - segmentKeepSeconds) / _segmentLength;
|
||||||
|
|
||||||
|
if (idxMaxToDelete > 0)
|
||||||
|
{
|
||||||
|
await DeleteSegmentFiles(_job, 0, idxMaxToDelete, 1500).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task DeleteSegmentFiles(TranscodingJob job, long idxMin, long idxMax, int delayMs)
|
||||||
|
{
|
||||||
|
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 (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogDebug(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<Exception>? 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<Exception>()).Add(ex);
|
||||||
|
_logger.LogDebug(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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -115,7 +115,7 @@ public class TranscodingThrottler : IDisposable
|
||||||
|
|
||||||
var options = GetOptions();
|
var options = GetOptions();
|
||||||
|
|
||||||
if (options.EnableThrottling && IsThrottleAllowed(_job, options.ThrottleDelaySeconds))
|
if (options.EnableThrottling && IsThrottleAllowed(_job, Math.Max(options.ThrottleDelaySeconds, 60)))
|
||||||
{
|
{
|
||||||
await PauseTranscoding().ConfigureAwait(false);
|
await PauseTranscoding().ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
|
@ -146,17 +146,18 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||||
{ 5, new string[] { "overlay_vulkan", "Action to take when encountering EOF from secondary input" } }
|
{ 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<string, Version> _ffmpegMinimumLibraryVersions = new Dictionary<string, Version>
|
private static readonly Dictionary<string, Version> _ffmpegMinimumLibraryVersions = new Dictionary<string, Version>
|
||||||
{
|
{
|
||||||
{ "libavutil", new Version(56, 14) },
|
{ "libavutil", new Version(56, 70) },
|
||||||
{ "libavcodec", new Version(58, 18) },
|
{ "libavcodec", new Version(58, 134) },
|
||||||
{ "libavformat", new Version(58, 12) },
|
{ "libavformat", new Version(58, 76) },
|
||||||
{ "libavdevice", new Version(58, 3) },
|
{ "libavdevice", new Version(58, 13) },
|
||||||
{ "libavfilter", new Version(7, 16) },
|
{ "libavfilter", new Version(7, 110) },
|
||||||
{ "libswscale", new Version(5, 1) },
|
{ "libswscale", new Version(5, 9) },
|
||||||
{ "libswresample", new Version(3, 1) },
|
{ "libswresample", new Version(3, 9) },
|
||||||
{ "libpostproc", new Version(55, 1) }
|
{ "libpostproc", new Version(55, 9) }
|
||||||
};
|
};
|
||||||
|
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
|
@ -176,7 +177,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||||
}
|
}
|
||||||
|
|
||||||
// When changing this, also change the minimum library versions in _ffmpegMinimumLibraryVersions
|
// 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;
|
public static Version? MaxVersion { get; } = null;
|
||||||
|
|
||||||
|
|
|
@ -321,7 +321,7 @@ public sealed class TranscodeManager : ITranscodeManager, IDisposable
|
||||||
}
|
}
|
||||||
catch (IOException ex)
|
catch (IOException ex)
|
||||||
{
|
{
|
||||||
(exs ??= new List<Exception>(4)).Add(ex);
|
(exs ??= new List<Exception>()).Add(ex);
|
||||||
_logger.LogError(ex, "Error deleting HLS file {Path}", file);
|
_logger.LogError(ex, "Error deleting HLS file {Path}", file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -546,6 +546,7 @@ public sealed class TranscodeManager : ITranscodeManager, IDisposable
|
||||||
if (!transcodingJob.HasExited)
|
if (!transcodingJob.HasExited)
|
||||||
{
|
{
|
||||||
StartThrottler(state, transcodingJob);
|
StartThrottler(state, transcodingJob);
|
||||||
|
StartSegmentCleaner(state, transcodingJob);
|
||||||
}
|
}
|
||||||
else if (transcodingJob.ExitCode != 0)
|
else if (transcodingJob.ExitCode != 0)
|
||||||
{
|
{
|
||||||
|
@ -573,6 +574,22 @@ public sealed class TranscodeManager : ITranscodeManager, IDisposable
|
||||||
&& state.IsInputVideo
|
&& state.IsInputVideo
|
||||||
&& state.VideoType == VideoType.VideoFile;
|
&& state.VideoType == VideoType.VideoFile;
|
||||||
|
|
||||||
|
private void StartSegmentCleaner(StreamState state, TranscodingJob transcodingJob)
|
||||||
|
{
|
||||||
|
if (EnableSegmentCleaning(state))
|
||||||
|
{
|
||||||
|
transcodingJob.TranscodingSegmentCleaner = new TranscodingSegmentCleaner(transcodingJob, _loggerFactory.CreateLogger<TranscodingSegmentCleaner>(), _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(
|
private TranscodingJob OnTranscodeBeginning(
|
||||||
string path,
|
string path,
|
||||||
string? playSessionId,
|
string? playSessionId,
|
||||||
|
|
|
@ -17,16 +17,11 @@ namespace Jellyfin.MediaEncoding.Tests
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
|
[InlineData(EncoderValidatorTestsData.FFmpegV611Output, true)]
|
||||||
[InlineData(EncoderValidatorTestsData.FFmpegV60Output, true)]
|
[InlineData(EncoderValidatorTestsData.FFmpegV60Output, true)]
|
||||||
[InlineData(EncoderValidatorTestsData.FFmpegV512Output, true)]
|
[InlineData(EncoderValidatorTestsData.FFmpegV512Output, true)]
|
||||||
[InlineData(EncoderValidatorTestsData.FFmpegV44Output, true)]
|
[InlineData(EncoderValidatorTestsData.FFmpegV44Output, true)]
|
||||||
[InlineData(EncoderValidatorTestsData.FFmpegV432Output, true)]
|
[InlineData(EncoderValidatorTestsData.FFmpegV432Output, false)]
|
||||||
[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.FFmpegGitUnknownOutput2, true)]
|
[InlineData(EncoderValidatorTestsData.FFmpegGitUnknownOutput2, true)]
|
||||||
[InlineData(EncoderValidatorTestsData.FFmpegGitUnknownOutput, false)]
|
[InlineData(EncoderValidatorTestsData.FFmpegGitUnknownOutput, false)]
|
||||||
public void ValidateVersionInternalTest(string versionOutput, bool valid)
|
public void ValidateVersionInternalTest(string versionOutput, bool valid)
|
||||||
|
@ -38,17 +33,12 @@ namespace Jellyfin.MediaEncoding.Tests
|
||||||
{
|
{
|
||||||
public GetFFmpegVersionTestData()
|
public GetFFmpegVersionTestData()
|
||||||
{
|
{
|
||||||
|
Add(EncoderValidatorTestsData.FFmpegV611Output, new Version(6, 1, 1));
|
||||||
Add(EncoderValidatorTestsData.FFmpegV60Output, new Version(6, 0));
|
Add(EncoderValidatorTestsData.FFmpegV60Output, new Version(6, 0));
|
||||||
Add(EncoderValidatorTestsData.FFmpegV512Output, new Version(5, 1, 2));
|
Add(EncoderValidatorTestsData.FFmpegV512Output, new Version(5, 1, 2));
|
||||||
Add(EncoderValidatorTestsData.FFmpegV44Output, new Version(4, 4));
|
Add(EncoderValidatorTestsData.FFmpegV44Output, new Version(4, 4));
|
||||||
Add(EncoderValidatorTestsData.FFmpegV432Output, new Version(4, 3, 2));
|
Add(EncoderValidatorTestsData.FFmpegV432Output, new Version(4, 3, 2));
|
||||||
Add(EncoderValidatorTestsData.FFmpegV431Output, new Version(4, 3, 1));
|
Add(EncoderValidatorTestsData.FFmpegGitUnknownOutput2, new Version(4, 4));
|
||||||
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.FFmpegGitUnknownOutput, null);
|
Add(EncoderValidatorTestsData.FFmpegGitUnknownOutput, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,18 @@ namespace Jellyfin.MediaEncoding.Tests
|
||||||
{
|
{
|
||||||
internal static class EncoderValidatorTestsData
|
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
|
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)
|
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
|
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
|
libswresample 3. 7.100 / 3. 7.100
|
||||||
libpostproc 55. 7.100 / 55. 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
|
public const string FFmpegGitUnknownOutput2 = @"ffmpeg version N-g01fc3034ee-20240317 Copyright (c) 2000-2023 the FFmpeg developers
|
||||||
built with gcc 10.1.0 (GCC)
|
built with gcc 13.2.0 (crosstool-NG 1.26.0.65_ecc5e41)
|
||||||
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
|
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. 51.100 / 56. 51.100
|
libavutil 56. 70.100 / 56. 70.100
|
||||||
libavcodec 58. 91.100 / 58. 91.100
|
libavcodec 58.134.100 / 58.134.100
|
||||||
libavformat 58. 45.100 / 58. 45.100
|
libavformat 58. 76.100 / 58. 76.100
|
||||||
libavdevice 58. 10.100 / 58. 10.100
|
libavdevice 58. 13.100 / 58. 13.100
|
||||||
libavfilter 7. 85.100 / 7. 85.100
|
libavfilter 7.110.100 / 7.110.100
|
||||||
libswscale 5. 7.100 / 5. 7.100
|
libswscale 5. 9.100 / 5. 9.100
|
||||||
libswresample 3. 7.100 / 3. 7.100
|
libswresample 3. 9.100 / 3. 9.100
|
||||||
libpostproc 55. 7.100 / 55. 7.100";
|
libpostproc 55. 9.100 / 55. 9.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 FFmpegGitUnknownOutput = @"ffmpeg version N-45325-gb173e0353-static https://johnvansickle.com/ffmpeg/ Copyright (c) 2000-2018 the FFmpeg developers
|
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
|
built with gcc 6.3.0 (Debian 6.3.0-18+deb9u1) 20170516
|
||||||
|
|
Loading…
Reference in New Issue