mirror of
https://github.com/rosenbjerg/FFMpegCore.git
synced 2025-12-14 10:05:44 +00:00
Merge f919a05d43 into bd55ec2a51
This commit is contained in:
commit
bd903bdfd8
15 changed files with 383 additions and 3 deletions
9
FFMpegCore.Extensions.Downloader/Enums/FFMpegBinaries.cs
Normal file
9
FFMpegCore.Extensions.Downloader/Enums/FFMpegBinaries.cs
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
namespace FFMpegCore.Extensions.Downloader.Enums;
|
||||
|
||||
[Flags]
|
||||
public enum FFMpegBinaries : ushort
|
||||
{
|
||||
FFMpeg,
|
||||
FFProbe,
|
||||
FFPlay
|
||||
}
|
||||
39
FFMpegCore.Extensions.Downloader/Enums/FFMpegVersions.cs
Normal file
39
FFMpegCore.Extensions.Downloader/Enums/FFMpegVersions.cs
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
using System.ComponentModel;
|
||||
|
||||
namespace FFMpegCore.Extensions.Downloader.Enums;
|
||||
|
||||
public enum FFMpegVersions : ushort
|
||||
{
|
||||
[Description("https://ffbinaries.com/api/v1/version/latest")]
|
||||
Latest,
|
||||
|
||||
[Description("https://ffbinaries.com/api/v1/version/6.1")]
|
||||
V6_1,
|
||||
|
||||
[Description("https://ffbinaries.com/api/v1/version/5.1")]
|
||||
V5_1,
|
||||
|
||||
[Description("https://ffbinaries.com/api/v1/version/4.4.1")]
|
||||
V4_4_1,
|
||||
|
||||
[Description("https://ffbinaries.com/api/v1/version/4.2.1")]
|
||||
V4_2_1,
|
||||
|
||||
[Description("https://ffbinaries.com/api/v1/version/4.2")]
|
||||
V4_2,
|
||||
|
||||
[Description("https://ffbinaries.com/api/v1/version/4.1")]
|
||||
V4_1,
|
||||
|
||||
[Description("https://ffbinaries.com/api/v1/version/4.0")]
|
||||
V4_0,
|
||||
|
||||
[Description("https://ffbinaries.com/api/v1/version/3.4")]
|
||||
V3_4,
|
||||
|
||||
[Description("https://ffbinaries.com/api/v1/version/3.3")]
|
||||
V3_3,
|
||||
|
||||
[Description("https://ffbinaries.com/api/v1/version/3.2")]
|
||||
V3_2
|
||||
}
|
||||
13
FFMpegCore.Extensions.Downloader/Enums/SupportedPlatforms.cs
Normal file
13
FFMpegCore.Extensions.Downloader/Enums/SupportedPlatforms.cs
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
namespace FFMpegCore.Extensions.Downloader.Enums;
|
||||
|
||||
public enum SupportedPlatforms : ushort
|
||||
{
|
||||
Windows64,
|
||||
Windows32,
|
||||
Linux64,
|
||||
Linux32,
|
||||
LinuxArmhf,
|
||||
LinuxArmel,
|
||||
LinuxArm64,
|
||||
Osx64
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
namespace FFMpegCore.Extensions.Downloader.Exceptions;
|
||||
|
||||
/// <summary>
|
||||
/// Custom exception for FFMpegDownloader
|
||||
/// </summary>
|
||||
public class FFMpegDownloaderException : Exception
|
||||
{
|
||||
public string Detail { get; set; } = "";
|
||||
|
||||
public FFMpegDownloaderException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public FFMpegDownloaderException(string message, string detail) : base(message)
|
||||
{
|
||||
Detail = detail;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
using System.ComponentModel;
|
||||
|
||||
namespace FFMpegCore.Extensions.Downloader.Extensions;
|
||||
|
||||
internal static class EnumExtensions
|
||||
{
|
||||
public static string GetDescription(this Enum enumValue)
|
||||
{
|
||||
var field = enumValue.GetType().GetField(enumValue.ToString());
|
||||
if (field == null)
|
||||
{
|
||||
return enumValue.ToString();
|
||||
}
|
||||
|
||||
if (Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute)) is DescriptionAttribute attribute)
|
||||
{
|
||||
return attribute.Description;
|
||||
}
|
||||
|
||||
return enumValue.ToString();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.1</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<IsPackable>true</IsPackable>
|
||||
<Description>FFMpeg downloader extension for FFMpegCore</Description>
|
||||
<PackageVersion>5.0.0</PackageVersion>
|
||||
<PackageOutputPath>../nupkg</PackageOutputPath>
|
||||
<PackageReleaseNotes>
|
||||
</PackageReleaseNotes>
|
||||
<PackageTags>ffmpeg ffprobe convert video audio mediafile resize analyze download</PackageTags>
|
||||
<Authors>Malte Rosenbjerg, Vlad Jerca, Max Bagryantsev, Kerry Cao</Authors>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\FFMpegCore\FFMpegCore.csproj"/>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
53
FFMpegCore.Extensions.Downloader/FFMpegDownloader.cs
Normal file
53
FFMpegCore.Extensions.Downloader/FFMpegDownloader.cs
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
using FFMpegCore.Extensions.Downloader.Enums;
|
||||
using FFMpegCore.Extensions.Downloader.Exceptions;
|
||||
using FFMpegCore.Extensions.Downloader.Services;
|
||||
|
||||
namespace FFMpegCore.Extensions.Downloader;
|
||||
|
||||
public class FFMpegDownloader
|
||||
{
|
||||
/// <summary>
|
||||
/// Download the latest FFMpeg suite binaries for current platform
|
||||
/// </summary>
|
||||
/// <param name="version">used to explicitly state the version of binary you want to download</param>
|
||||
/// <param name="binaries">used to explicitly state the binaries you want to download (ffmpeg, ffprobe, ffplay)</param>
|
||||
/// <param name="platformOverride">used to explicitly state the os and architecture you want to download</param>
|
||||
/// <returns>a list of the binaries that have been successfully downloaded</returns>
|
||||
public static async Task<List<string>> DownloadFFMpegSuite(
|
||||
FFMpegVersions version = FFMpegVersions.Latest,
|
||||
FFMpegBinaries binaries = FFMpegBinaries.FFMpeg | FFMpegBinaries.FFProbe,
|
||||
SupportedPlatforms? platformOverride = null)
|
||||
{
|
||||
// get all available versions
|
||||
var versionInfo = await FFbinariesService.GetVersionInfo(version);
|
||||
|
||||
// get the download info for the current platform
|
||||
var downloadInfo = versionInfo.BinaryInfo?.GetCompatibleDownloadInfo(platformOverride) ??
|
||||
throw new FFMpegDownloaderException("Failed to get compatible download info");
|
||||
|
||||
var successList = new List<string>();
|
||||
|
||||
// download ffmpeg if selected
|
||||
if (binaries.HasFlag(FFMpegBinaries.FFMpeg) && downloadInfo.FFMpeg is not null)
|
||||
{
|
||||
await using var zipStream = await FFbinariesService.DownloadFileAsSteam(new Uri(downloadInfo.FFMpeg));
|
||||
successList.AddRange(FFbinariesService.ExtractZipAndSave(zipStream));
|
||||
}
|
||||
|
||||
// download ffprobe if selected
|
||||
if (binaries.HasFlag(FFMpegBinaries.FFProbe) && downloadInfo.FFProbe is not null)
|
||||
{
|
||||
await using var zipStream = await FFbinariesService.DownloadFileAsSteam(new Uri(downloadInfo.FFProbe));
|
||||
successList.AddRange(FFbinariesService.ExtractZipAndSave(zipStream));
|
||||
}
|
||||
|
||||
// download ffplay if selected
|
||||
if (binaries.HasFlag(FFMpegBinaries.FFPlay) && downloadInfo.FFPlay is not null)
|
||||
{
|
||||
await using var zipStream = await FFbinariesService.DownloadFileAsSteam(new Uri(downloadInfo.FFPlay));
|
||||
successList.AddRange(FFbinariesService.ExtractZipAndSave(zipStream));
|
||||
}
|
||||
|
||||
return successList;
|
||||
}
|
||||
}
|
||||
74
FFMpegCore.Extensions.Downloader/Models/BinaryInfo.cs
Normal file
74
FFMpegCore.Extensions.Downloader/Models/BinaryInfo.cs
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
using System.Runtime.InteropServices;
|
||||
using System.Text.Json.Serialization;
|
||||
using FFMpegCore.Extensions.Downloader.Enums;
|
||||
|
||||
namespace FFMpegCore.Extensions.Downloader.Models;
|
||||
|
||||
internal record BinaryInfo
|
||||
{
|
||||
[JsonPropertyName("windows-64")] public DownloadInfo? Windows64 { get; set; }
|
||||
|
||||
[JsonPropertyName("windows-32")] public DownloadInfo? Windows32 { get; set; }
|
||||
|
||||
[JsonPropertyName("linux-32")] public DownloadInfo? Linux32 { get; set; }
|
||||
|
||||
[JsonPropertyName("linux-64")] public DownloadInfo? Linux64 { get; set; }
|
||||
|
||||
[JsonPropertyName("linux-armhf")] public DownloadInfo? LinuxArmhf { get; set; }
|
||||
|
||||
[JsonPropertyName("linux-armel")] public DownloadInfo? LinuxArmel { get; set; }
|
||||
|
||||
[JsonPropertyName("linux-arm64")] public DownloadInfo? LinuxArm64 { get; set; }
|
||||
|
||||
[JsonPropertyName("osx-64")] public DownloadInfo? Osx64 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Automatically get the compatible download info for current os and architecture
|
||||
/// </summary>
|
||||
/// <param name="platformOverride"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="ArgumentOutOfRangeException"></exception>
|
||||
/// <exception cref="PlatformNotSupportedException"></exception>
|
||||
public DownloadInfo? GetCompatibleDownloadInfo(SupportedPlatforms? platformOverride = null)
|
||||
{
|
||||
if (platformOverride is not null)
|
||||
{
|
||||
return platformOverride switch
|
||||
{
|
||||
SupportedPlatforms.Windows64 => Windows64,
|
||||
SupportedPlatforms.Windows32 => Windows32,
|
||||
SupportedPlatforms.Linux64 => Linux64,
|
||||
SupportedPlatforms.Linux32 => Linux32,
|
||||
SupportedPlatforms.LinuxArmhf => LinuxArmhf,
|
||||
SupportedPlatforms.LinuxArmel => LinuxArmel,
|
||||
SupportedPlatforms.LinuxArm64 => LinuxArm64,
|
||||
SupportedPlatforms.Osx64 => Osx64,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(platformOverride), platformOverride, null)
|
||||
};
|
||||
}
|
||||
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
return RuntimeInformation.OSArchitecture == Architecture.X64 ? Windows64 : Windows32;
|
||||
}
|
||||
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
||||
{
|
||||
return RuntimeInformation.OSArchitecture switch
|
||||
{
|
||||
Architecture.X86 => Linux32,
|
||||
Architecture.X64 => Linux64,
|
||||
Architecture.Arm => LinuxArmhf,
|
||||
Architecture.Arm64 => LinuxArm64,
|
||||
_ => LinuxArmel
|
||||
};
|
||||
}
|
||||
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
||||
{
|
||||
return Osx64;
|
||||
}
|
||||
|
||||
throw new PlatformNotSupportedException("Unsupported OS or Architecture");
|
||||
}
|
||||
}
|
||||
12
FFMpegCore.Extensions.Downloader/Models/DownloadInfo.cs
Normal file
12
FFMpegCore.Extensions.Downloader/Models/DownloadInfo.cs
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace FFMpegCore.Extensions.Downloader.Models;
|
||||
|
||||
internal record DownloadInfo
|
||||
{
|
||||
[JsonPropertyName("ffmpeg")] public string? FFMpeg { get; set; }
|
||||
|
||||
[JsonPropertyName("ffprobe")] public string? FFProbe { get; set; }
|
||||
|
||||
[JsonPropertyName("ffplay")] public string? FFPlay { get; set; }
|
||||
}
|
||||
12
FFMpegCore.Extensions.Downloader/Models/VersionInfo.cs
Normal file
12
FFMpegCore.Extensions.Downloader/Models/VersionInfo.cs
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace FFMpegCore.Extensions.Downloader.Models;
|
||||
|
||||
internal record VersionInfo
|
||||
{
|
||||
[JsonPropertyName("version")] public string? Version { get; set; }
|
||||
|
||||
[JsonPropertyName("permalink")] public string? Permalink { get; set; }
|
||||
|
||||
[JsonPropertyName("bin")] public BinaryInfo? BinaryInfo { get; set; }
|
||||
}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
using System.IO.Compression;
|
||||
using System.Text.Json;
|
||||
using FFMpegCore.Extensions.Downloader.Enums;
|
||||
using FFMpegCore.Extensions.Downloader.Exceptions;
|
||||
using FFMpegCore.Extensions.Downloader.Extensions;
|
||||
using FFMpegCore.Extensions.Downloader.Models;
|
||||
|
||||
namespace FFMpegCore.Extensions.Downloader.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Service to interact with ffbinaries.com API
|
||||
/// </summary>
|
||||
internal class FFbinariesService
|
||||
{
|
||||
/// <summary>
|
||||
/// Get version info from ffbinaries.com
|
||||
/// </summary>
|
||||
/// <param name="version">use to explicitly state the version of ffmpeg you want</param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="FFMpegDownloaderException"></exception>
|
||||
internal static async Task<VersionInfo> GetVersionInfo(FFMpegVersions version)
|
||||
{
|
||||
var versionUri = version.GetDescription();
|
||||
|
||||
HttpClient client = new();
|
||||
var response = await client.GetAsync(versionUri);
|
||||
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
throw new FFMpegDownloaderException($"Failed to get version info from {versionUri}", "network error");
|
||||
}
|
||||
|
||||
var jsonString = await response.Content.ReadAsStringAsync();
|
||||
var versionInfo = JsonSerializer.Deserialize<VersionInfo>(jsonString);
|
||||
|
||||
return versionInfo ??
|
||||
throw new FFMpegDownloaderException($"Failed to deserialize version info from {versionUri}", jsonString);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Download file from uri
|
||||
/// </summary>
|
||||
/// <param name="address">uri of the file</param>
|
||||
/// <returns></returns>
|
||||
internal static async Task<Stream> DownloadFileAsSteam(Uri address)
|
||||
{
|
||||
var client = new HttpClient();
|
||||
return await client.GetStreamAsync(address);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extracts the binaries from the zip stream and saves them to the current binary folder
|
||||
/// </summary>
|
||||
/// <param name="zipStream">steam of the zip file</param>
|
||||
/// <returns></returns>
|
||||
internal static IEnumerable<string> ExtractZipAndSave(Stream zipStream)
|
||||
{
|
||||
using var archive = new ZipArchive(zipStream, ZipArchiveMode.Read);
|
||||
List<string> files = new();
|
||||
foreach (var entry in archive.Entries)
|
||||
{
|
||||
if (entry.Name is "ffmpeg" or "ffmpeg.exe" or "ffprobe.exe" or "ffprobe" or "ffplay.exe" or "ffplay")
|
||||
{
|
||||
entry.ExtractToFile(Path.Combine(GlobalFFOptions.Current.BinaryFolder, entry.Name), true);
|
||||
files.Add(Path.Combine(GlobalFFOptions.Current.BinaryFolder, entry.Name));
|
||||
}
|
||||
}
|
||||
|
||||
return files;
|
||||
}
|
||||
}
|
||||
22
FFMpegCore.Test/DownloaderTests.cs
Normal file
22
FFMpegCore.Test/DownloaderTests.cs
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
using FFMpegCore.Extensions.Downloader;
|
||||
using FFMpegCore.Extensions.Downloader.Enums;
|
||||
|
||||
namespace FFMpegCore.Test;
|
||||
|
||||
[TestClass]
|
||||
public class DownloaderTests
|
||||
{
|
||||
[TestMethod]
|
||||
public async Task GetSpecificVersionTest()
|
||||
{
|
||||
var binaries = await FFMpegDownloader.DownloadFFMpegSuite(FFMpegVersions.V6_1);
|
||||
Assert.HasCount(2, binaries);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task GetAllLatestSuiteTest()
|
||||
{
|
||||
var binaries = await FFMpegDownloader.DownloadFFMpegSuite();
|
||||
Assert.HasCount(2, binaries);
|
||||
}
|
||||
}
|
||||
|
|
@ -24,6 +24,7 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\FFMpegCore.Extensions.Downloader\FFMpegCore.Extensions.Downloader.csproj" />
|
||||
<ProjectReference Include="..\FFMpegCore.Extensions.SkiaSharp\FFMpegCore.Extensions.SkiaSharp.csproj"/>
|
||||
<ProjectReference Include="..\FFMpegCore.Extensions.System.Drawing.Common\FFMpegCore.Extensions.System.Drawing.Common.csproj"/>
|
||||
<ProjectReference Include="..\FFMpegCore\FFMpegCore.csproj"/>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.31005.135
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.7.34003.232
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FFMpegCore", "FFMpegCore\FFMpegCore.csproj", "{19DE2EC2-9955-4712-8096-C22EF6713E4F}"
|
||||
EndProject
|
||||
|
|
@ -13,6 +13,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FFMpegCore.Extensions.Syste
|
|||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FFMpegCore.Extensions.SkiaSharp", "FFMpegCore.Extensions.SkiaSharp\FFMpegCore.Extensions.SkiaSharp.csproj", "{5A76F9B7-3681-4551-A9B6-8D3AC5DA1090}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FFMpegCore.Extensions.Downloader", "FFMpegCore.Extensions.Downloader\FFMpegCore.Extensions.Downloader.csproj", "{5FA30158-CAB0-44FD-AD98-C31F5E3D5A56}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
|
@ -39,6 +41,10 @@ Global
|
|||
{5A76F9B7-3681-4551-A9B6-8D3AC5DA1090}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{5A76F9B7-3681-4551-A9B6-8D3AC5DA1090}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{5A76F9B7-3681-4551-A9B6-8D3AC5DA1090}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{5FA30158-CAB0-44FD-AD98-C31F5E3D5A56}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{5FA30158-CAB0-44FD-AD98-C31F5E3D5A56}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{5FA30158-CAB0-44FD-AD98-C31F5E3D5A56}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{5FA30158-CAB0-44FD-AD98-C31F5E3D5A56}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
|
|
|||
|
|
@ -184,7 +184,12 @@ If you want to use `System.Drawing.Bitmap`s as `IVideoFrame`s, a `BitmapVideoFra
|
|||
|
||||
# Binaries
|
||||
|
||||
## Installation
|
||||
## Runtime Auto Installation
|
||||
You can install a version of ffmpeg suite at runtime using `FFMpegDownloader.DownloadFFMpegSuite();`
|
||||
|
||||
This feature uses the api from [ffbinaries](https://ffbinaries.com/api).
|
||||
|
||||
## Manual Installation
|
||||
|
||||
If you prefer to manually download them, visit [ffbinaries](https://ffbinaries.com/downloads)
|
||||
or [zeranoe Windows builds](https://ffmpeg.zeranoe.com/builds/).
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue