Add Directory.Build.props

This commit is contained in:
Malte Rosenbjerg 2023-01-29 22:17:40 +01:00
parent 2abb2a1e6b
commit 8e9b7df4de
9 changed files with 135 additions and 234 deletions

15
Directory.Build.props Normal file
View file

@ -0,0 +1,15 @@
<Project>
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<NeutralLanguage>en</NeutralLanguage>
<AssemblyVersion>5.0.0.0</AssemblyVersion>
<LangVersion>default</LangVersion>
<Nullable>enable</Nullable>
<RepositoryType>GitHub</RepositoryType>
<RepositoryUrl>https://github.com/rosenbjerg/FFMpegCore</RepositoryUrl>
<PackageProjectUrl>https://github.com/rosenbjerg/FFMpegCore</PackageProjectUrl>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<NeutralLanguage>en</NeutralLanguage>
</PropertyGroup>
</Project>

View file

@ -85,7 +85,7 @@ await FFMpegArguments
{ {
FFMpegImage.PosterWithAudio(inputPath, inputAudioPath, outputPath); FFMpegImage.PosterWithAudio(inputPath, inputAudioPath, outputPath);
// or // or
var image = Image.FromFile(inputImagePath); using var image = Image.FromFile(inputImagePath);
image.AddAudio(inputAudioPath, outputPath); image.AddAudio(inputAudioPath, outputPath);
} }

View file

@ -12,7 +12,7 @@ public static bool AddAudio(this Image poster, string audio, string output)
poster.Save(destination); poster.Save(destination);
try try
{ {
return FFMpeg.PosterWithAudio(destination, audio, output); return FFMpegImage.PosterWithAudio(destination, audio, output);
} }
finally finally
{ {

View file

@ -1,28 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<NeutralLanguage>en</NeutralLanguage> <IsPackable>true</IsPackable>
<RepositoryUrl>https://github.com/rosenbjerg/FFMpegCore</RepositoryUrl> <Description>Image extension for FFMpegCore using System.Common.Drawing</Description>
<PackageProjectUrl>https://github.com/rosenbjerg/FFMpegCore</PackageProjectUrl> <PackageVersion>5.0.0</PackageVersion>
<Copyright></Copyright>
<Description>Image extension for FFMpegCore, using System.Common.Drawing</Description>
<PackageReleaseNotes> <PackageReleaseNotes>
</PackageReleaseNotes> </PackageReleaseNotes>
<LangVersion>8</LangVersion>
<AssemblyVersion>4.0.0.0</AssemblyVersion>
<PackageVersion>4.0.0</PackageVersion>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<Authors>Malte Rosenbjerg, Vlad Jerca, Max Bagryantsev</Authors>
<PackageTags>ffmpeg ffprobe convert video audio mediafile resize analyze muxing</PackageTags> <PackageTags>ffmpeg ffprobe convert video audio mediafile resize analyze muxing</PackageTags>
<RepositoryType>GitHub</RepositoryType> <Authors>Malte Rosenbjerg, Vlad Jerca, Max Bagryantsev</Authors>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<IsPackable>true</IsPackable>
<Nullable>enable</Nullable>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="System.Drawing.Common" Version="6.0.0" /> <PackageReference Include="System.Drawing.Common" Version="7.0.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View file

@ -41,6 +41,7 @@ public static bool JoinImageSequence(string output, double frameRate = 30, param
return FFMpegArguments return FFMpegArguments
.FromFileInput(Path.Combine(tempFolderName, "%09d.png"), false) .FromFileInput(Path.Combine(tempFolderName, "%09d.png"), false)
.OutputToFile(output, true, options => options .OutputToFile(output, true, options => options
.ForcePixelFormat("yuv420p")
.Resize(firstImage.Width, firstImage.Height) .Resize(firstImage.Width, firstImage.Height)
.WithFramerate(frameRate)) .WithFramerate(frameRate))
.ProcessSynchronously(); .ProcessSynchronously();
@ -62,22 +63,22 @@ public static bool PosterWithAudio(string image, string audio, string output)
{ {
FFMpegHelper.ExtensionExceptionCheck(output, FileExtension.Mp4); FFMpegHelper.ExtensionExceptionCheck(output, FileExtension.Mp4);
using (var img = Image.FromFile(image)) using (var img = Image.FromFile(image))
FFMpegHelper.ConversionSizeExceptionCheck(img); FFMpegHelper.ConversionSizeExceptionCheck(img.Width, img.Height);
return FFMpegArguments return FFMpegArguments
.FromFileInput(image, false, options => options .FromFileInput(image, false, options => options
.Loop(1)) .Loop(1)
.ForceFormat("image2"))
.AddFileInput(audio) .AddFileInput(audio)
.OutputToFile(output, true, options => options .OutputToFile(output, true, options => options
.ForcePixelFormat("yuv420p")
.WithVideoCodec(VideoCodec.LibX264) .WithVideoCodec(VideoCodec.LibX264)
.CopyChannel()
.WithConstantRateFactor(21) .WithConstantRateFactor(21)
.WithAudioBitrate(AudioQuality.Normal) .WithAudioBitrate(AudioQuality.Normal)
.UsingShortest()) .UsingShortest())
.ProcessSynchronously(); .ProcessSynchronously();
} }
/// <summary> /// <summary>
/// Saves a 'png' thumbnail to an in-memory bitmap /// Saves a 'png' thumbnail to an in-memory bitmap
/// </summary> /// </summary>
@ -90,7 +91,7 @@ public static bool PosterWithAudio(string image, string audio, string output)
public static Bitmap Snapshot(string input, Size? size = null, TimeSpan? captureTime = null, int? streamIndex = null, int inputFileIndex = 0) public static Bitmap Snapshot(string input, Size? size = null, TimeSpan? captureTime = null, int? streamIndex = null, int inputFileIndex = 0)
{ {
var source = FFProbe.Analyse(input); var source = FFProbe.Analyse(input);
var (arguments, outputOptions) = BuildSnapshotArguments(input, source, size, captureTime, streamIndex, inputFileIndex); var (arguments, outputOptions) = SnapshotArgumentBuilder.BuildSnapshotArguments(input, source, size, captureTime, streamIndex, inputFileIndex);
using var ms = new MemoryStream(); using var ms = new MemoryStream();
arguments arguments
@ -114,7 +115,7 @@ public static Bitmap Snapshot(string input, Size? size = null, TimeSpan? capture
public static async Task<Bitmap> SnapshotAsync(string input, Size? size = null, TimeSpan? captureTime = null, int? streamIndex = null, int inputFileIndex = 0) public static async Task<Bitmap> SnapshotAsync(string input, Size? size = null, TimeSpan? captureTime = null, int? streamIndex = null, int inputFileIndex = 0)
{ {
var source = await FFProbe.AnalyseAsync(input).ConfigureAwait(false); var source = await FFProbe.AnalyseAsync(input).ConfigureAwait(false);
var (arguments, outputOptions) = BuildSnapshotArguments(input, source, size, captureTime, streamIndex, inputFileIndex); var (arguments, outputOptions) = SnapshotArgumentBuilder.BuildSnapshotArguments(input, source, size, captureTime, streamIndex, inputFileIndex);
using var ms = new MemoryStream(); using var ms = new MemoryStream();
await arguments await arguments

View file

@ -2,42 +2,11 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
<Nullable>disable</Nullable> <Nullable>disable</Nullable>
<LangVersion>default</LangVersion> <LangVersion>default</LangVersion>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<None Remove="ffmpeg.config.json" />
<None Update="Resources\input.webm">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\input_3sec.mp4">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\input_3sec.webm">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\input_audio_only_10sec.mp4">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\input_video_only_3sec.mp4">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\audio.raw">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<Content Include="ffmpeg.config.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="FluentAssertions" Version="6.9.0" /> <PackageReference Include="FluentAssertions" Version="6.9.0" />
<PackageReference Include="GitHubActionsTestLogger" Version="2.0.1"> <PackageReference Include="GitHubActionsTestLogger" Version="2.0.1">
@ -55,46 +24,60 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Update="Resources\input.webm">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\input_3sec.mp4">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\input_3sec.webm">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\input_audio_only_10sec.mp4">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\input_video_only_3sec.mp4">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\audio.raw">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\audio.mp3"> <None Update="Resources\audio.mp3">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>
<None Update="Resources\audio_only.mp4"> <None Update="Resources\audio_only.mp4">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>
<None Update="Resources\cover.png"> <None Update="Resources\cover.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>
<None Update="Resources\images\a.png"> <None Update="Resources\images\a.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>
<None Update="Resources\images\b.png"> <None Update="Resources\images\b.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>
<None Update="Resources\images\c.png"> <None Update="Resources\images\c.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>
<None Update="Resources\images\d.png"> <None Update="Resources\images\d.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>
<None Update="Resources\images\e.png"> <None Update="Resources\images\e.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>
<None Update="Resources\images\f.png"> <None Update="Resources\images\f.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>
<None Update="Resources\input.mp4"> <None Update="Resources\input.mp4">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>
<None Update="Resources\mute.mp4"> <None Update="Resources\mute.mp4">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>
<None Update="Resources\sample.srt"> <None Update="Resources\sample.srt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>
</ItemGroup> </ItemGroup>
<ItemGroup>
<Folder Include="Properties\" />
</ItemGroup>
</Project> </Project>

View file

@ -34,8 +34,8 @@ private string GetText()
public interface IAudioFilterArgument public interface IAudioFilterArgument
{ {
public string Key { get; } string Key { get; }
public string Value { get; } string Value { get; }
} }
public class AudioFilterOptions public class AudioFilterOptions

View file

@ -1,7 +1,6 @@
using FFMpegCore.Enums; using FFMpegCore.Enums;
using FFMpegCore.Exceptions; using FFMpegCore.Exceptions;
using FFMpegCore.Helpers; using FFMpegCore.Helpers;
using FFMpegCore.Pipes;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Drawing; using System.Drawing;
@ -12,54 +11,9 @@
namespace FFMpegCore namespace FFMpegCore
{ {
public static class FFMpeg public static class SnapshotArgumentBuilder
{ {
/// <summary> public static (FFMpegArguments, Action<FFMpegArgumentOptions> outputOptions) BuildSnapshotArguments(
/// Saves a 'png' thumbnail from the input video to drive
/// </summary>
/// <param name="input">Source video analysis</param>
/// <param name="output">Output video file path</param>
/// <param name="captureTime">Seek position where the thumbnail should be taken.</param>
/// <param name="size">Thumbnail size. If width or height equal 0, the other will be computed automatically.</param>
/// <param name="streamIndex">Selected video stream index.</param>
/// <param name="inputFileIndex">Input file index</param>
/// <returns>Bitmap with the requested snapshot.</returns>
public static bool Snapshot(string input, string output, Size? size = null, TimeSpan? captureTime = null, int? streamIndex = null, int inputFileIndex = 0)
{
if (Path.GetExtension(output) != FileExtension.Png)
output = Path.GetFileNameWithoutExtension(output) + FileExtension.Png;
var source = FFProbe.Analyse(input);
var (arguments, outputOptions) = BuildSnapshotArguments(input, source, size, captureTime, streamIndex, inputFileIndex);
return arguments
.OutputToFile(output, true, outputOptions)
.ProcessSynchronously();
}
/// <summary>
/// Saves a 'png' thumbnail from the input video to drive
/// </summary>
/// <param name="input">Source video analysis</param>
/// <param name="output">Output video file path</param>
/// <param name="captureTime">Seek position where the thumbnail should be taken.</param>
/// <param name="size">Thumbnail size. If width or height equal 0, the other will be computed automatically.</param>
/// <param name="streamIndex">Selected video stream index.</param>
/// <param name="inputFileIndex">Input file index</param>
/// <returns>Bitmap with the requested snapshot.</returns>
public static async Task<bool> SnapshotAsync(string input, string output, Size? size = null, TimeSpan? captureTime = null, int? streamIndex = null, int inputFileIndex = 0)
{
if (Path.GetExtension(output) != FileExtension.Png)
output = Path.GetFileNameWithoutExtension(output) + FileExtension.Png;
var source = await FFProbe.AnalyseAsync(input).ConfigureAwait(false);
var (arguments, outputOptions) = BuildSnapshotArguments(input, source, size, captureTime, streamIndex, inputFileIndex);
return await arguments
.OutputToFile(output, true, outputOptions)
.ProcessAsynchronously();
}
private static (FFMpegArguments, Action<FFMpegArgumentOptions> outputOptions) BuildSnapshotArguments(
string input, string input,
IMediaAnalysis source, IMediaAnalysis source,
Size? size = null, Size? size = null,
@ -109,13 +63,61 @@ private static (FFMpegArguments, Action<FFMpegArgumentOptions> outputOptions) Bu
return null; return null;
} }
}
public static class FFMpeg
{
/// <summary>
/// Saves a 'png' thumbnail from the input video to drive
/// </summary>
/// <param name="input">Source video analysis</param>
/// <param name="output">Output video file path</param>
/// <param name="captureTime">Seek position where the thumbnail should be taken.</param>
/// <param name="size">Thumbnail size. If width or height equal 0, the other will be computed automatically.</param>
/// <param name="streamIndex">Selected video stream index.</param>
/// <param name="inputFileIndex">Input file index</param>
/// <returns>Bitmap with the requested snapshot.</returns>
public static bool Snapshot(string input, string output, Size? size = null, TimeSpan? captureTime = null, int? streamIndex = null, int inputFileIndex = 0)
{
if (Path.GetExtension(output) != FileExtension.Png)
output = Path.GetFileNameWithoutExtension(output) + FileExtension.Png;
var source = FFProbe.Analyse(input);
var (arguments, outputOptions) = SnapshotArgumentBuilder.BuildSnapshotArguments(input, source, size, captureTime, streamIndex, inputFileIndex);
return arguments
.OutputToFile(output, true, outputOptions)
.ProcessSynchronously();
}
/// <summary>
/// Saves a 'png' thumbnail from the input video to drive
/// </summary>
/// <param name="input">Source video analysis</param>
/// <param name="output">Output video file path</param>
/// <param name="captureTime">Seek position where the thumbnail should be taken.</param>
/// <param name="size">Thumbnail size. If width or height equal 0, the other will be computed automatically.</param>
/// <param name="streamIndex">Selected video stream index.</param>
/// <param name="inputFileIndex">Input file index</param>
/// <returns>Bitmap with the requested snapshot.</returns>
public static async Task<bool> SnapshotAsync(string input, string output, Size? size = null, TimeSpan? captureTime = null, int? streamIndex = null, int inputFileIndex = 0)
{
if (Path.GetExtension(output) != FileExtension.Png)
output = Path.GetFileNameWithoutExtension(output) + FileExtension.Png;
var source = await FFProbe.AnalyseAsync(input).ConfigureAwait(false);
var (arguments, outputOptions) = SnapshotArgumentBuilder.BuildSnapshotArguments(input, source, size, captureTime, streamIndex, inputFileIndex);
return await arguments
.OutputToFile(output, true, outputOptions)
.ProcessAsynchronously();
}
/// <summary> /// <summary>
/// Convert a video do a different format. /// Convert a video do a different format.
/// </summary> /// </summary>
/// <param name="source">Input video source.</param> /// <param name="input">Input video source.</param>
/// <param name="output">Output information.</param> /// <param name="output">Output information.</param>
/// <param name="type">Target conversion video type.</param> /// <param name="format">Target conversion video format.</param>
/// <param name="speed">Conversion target speed/quality (faster speed = lower quality).</param> /// <param name="speed">Conversion target speed/quality (faster speed = lower quality).</param>
/// <param name="size">Video size.</param> /// <param name="size">Video size.</param>
/// <param name="audioQuality">Conversion target audio quality.</param> /// <param name="audioQuality">Conversion target audio quality.</param>
@ -189,35 +191,6 @@ public static bool Convert(
}; };
} }
/// <summary>
/// Adds a poster image to an audio file.
/// </summary>
/// <param name="image">Source image file.</param>
/// <param name="audio">Source audio file.</param>
/// <param name="output">Output video file.</param>
/// <returns></returns>
public static bool PosterWithAudio(string image, string audio, string output)
{
FFMpegHelper.ExtensionExceptionCheck(output, FileExtension.Mp4);
using (var imageFile = Image.FromFile(image))
{
FFMpegHelper.ConversionSizeExceptionCheck(imageFile);
}
return FFMpegArguments
.FromFileInput(image, false, options => options
.Loop(1)
.ForceFormat("image2"))
.AddFileInput(audio)
.OutputToFile(output, true, options => options
.ForcePixelFormat("yuv420p")
.WithVideoCodec(VideoCodec.LibX264)
.WithConstantRateFactor(21)
.WithAudioBitrate(AudioQuality.Normal)
.UsingShortest())
.ProcessSynchronously();
}
/// <summary> /// <summary>
/// Joins a list of video files. /// Joins a list of video files.
/// </summary> /// </summary>
@ -251,44 +224,6 @@ public static bool Join(string output, params string[] videos)
} }
} }
/// <summary>
/// Converts an image sequence to a video.
/// </summary>
/// <param name="output">Output video file.</param>
/// <param name="frameRate">FPS</param>
/// <param name="images">Image sequence collection</param>
/// <returns>Output video information.</returns>
public static bool JoinImageSequence(string output, double frameRate = 30, params ImageInfo[] images)
{
var tempFolderName = Path.Combine(GlobalFFOptions.Current.TemporaryFilesFolder, Guid.NewGuid().ToString());
var temporaryImageFiles = images.Select((imageInfo, index) =>
{
using var image = Image.FromFile(imageInfo.FullName);
FFMpegHelper.ConversionSizeExceptionCheck(image);
var destinationPath = Path.Combine(tempFolderName, $"{index.ToString().PadLeft(9, '0')}{imageInfo.Extension}");
Directory.CreateDirectory(tempFolderName);
File.Copy(imageInfo.FullName, destinationPath);
return destinationPath;
}).ToArray();
var firstImage = images.First();
try
{
return FFMpegArguments
.FromFileInput(Path.Combine(tempFolderName, "%09d.png"), false)
.OutputToFile(output, true, options => options
.ForcePixelFormat("yuv420p")
.Resize(firstImage.Width, firstImage.Height)
.WithFramerate(frameRate))
.ProcessSynchronously();
}
finally
{
Cleanup(temporaryImageFiles);
Directory.Delete(tempFolderName);
}
}
/// <summary> /// <summary>
/// Records M3U8 streams to the specified output. /// Records M3U8 streams to the specified output.
/// </summary> /// </summary>
@ -397,15 +332,15 @@ public static IReadOnlyList<PixelFormat> GetPixelFormats()
return FFMpegCache.PixelFormats.Values.ToList().AsReadOnly(); return FFMpegCache.PixelFormats.Values.ToList().AsReadOnly();
} }
public static bool TryGetPixelFormat(string name, out PixelFormat fmt) public static bool TryGetPixelFormat(string name, out PixelFormat format)
{ {
if (!GlobalFFOptions.Current.UseCache) if (!GlobalFFOptions.Current.UseCache)
{ {
fmt = GetPixelFormatsInternal().FirstOrDefault(x => x.Name == name.ToLowerInvariant().Trim()); format = GetPixelFormatsInternal().FirstOrDefault(x => x.Name == name.ToLowerInvariant().Trim());
return fmt != null; return format != null;
} }
else else
return FFMpegCache.PixelFormats.TryGetValue(name, out fmt); return FFMpegCache.PixelFormats.TryGetValue(name, out format);
} }
public static PixelFormat GetPixelFormat(string name) public static PixelFormat GetPixelFormat(string name)

View file

@ -1,39 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<NeutralLanguage>en</NeutralLanguage>
<RepositoryUrl>https://github.com/rosenbjerg/FFMpegCore</RepositoryUrl>
<PackageProjectUrl>https://github.com/rosenbjerg/FFMpegCore</PackageProjectUrl>
<Copyright></Copyright>
<Description>A .NET Standard FFMpeg/FFProbe wrapper for easily integrating media analysis and conversion into your .NET applications</Description>
<AssemblyVersion>4.0.0.0</AssemblyVersion>
<PackageReadmeFile>README.md</PackageReadmeFile>
<PackageReleaseNotes>- Fixes for `MetaDataArgument` (thanks @JKamsker)
- Support for Audible `aaxc` (thanks @JKamsker)
- Include errordata in `IMediaAnalysis` (thanks @JKamsker)
- Pass `FFOptions` properly when using ffprobe (thanks @Notheisz57)
- CancellationToken support for `AnalyseAsync`
- Case-insensitive dictionaries for `Tags` and `Disposition`
- Fix for `PosterWithAudio`
- Fix for `JoinImageSequence`
- Updates to dependendies
- A lot of bug fixes</PackageReleaseNotes>
<LangVersion>8</LangVersion>
<PackageVersion>4.8.0</PackageVersion>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<Authors>Malte Rosenbjerg, Vlad Jerca, Max Bagryantsev</Authors>
<PackageTags>ffmpeg ffprobe convert video audio mediafile resize analyze muxing</PackageTags>
<RepositoryType>GitHub</RepositoryType>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<IsPackable>true</IsPackable> <IsPackable>true</IsPackable>
<Nullable>enable</Nullable> <Description>A .NET Standard FFMpeg/FFProbe wrapper for easily integrating media analysis and conversion into your .NET applications</Description>
<TargetFramework>netstandard2.0</TargetFramework> <PackageVersion>5.0.0</PackageVersion>
<PackageReleaseNotes>
</PackageReleaseNotes>
<PackageTags>ffmpeg ffprobe convert video audio mediafile resize analyze muxing</PackageTags>
<Authors>Malte Rosenbjerg, Vlad Jerca, Max Bagryantsev</Authors>
<PackageReadmeFile>README.md</PackageReadmeFile>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Content Include="FFMPEG\bin\**\*">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<None Include="..\README.md" Pack="true" PackagePath="\"/> <None Include="..\README.md" Pack="true" PackagePath="\"/>
</ItemGroup> </ItemGroup>