using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Globalization;
using System.IO;
using System.IO.Pipes;
using System.Linq;
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Text;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using FFMpegCore.Arguments;
using FFMpegCore.Builders.MetaData;
using FFMpegCore.Enums;
using FFMpegCore.Exceptions;
using FFMpegCore.Extend;
using FFMpegCore.Helpers;
using FFMpegCore.Pipes;
using Instances;
using Instances.Exceptions;
using Microsoft.CodeAnalysis;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: InternalsVisibleTo("FFMpegCore.Test")]
[assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = ".NET Standard 2.0")]
[assembly: AssemblyCompany("Malte Rosenbjerg, Vlad Jerca, Max Bagryantsev")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("A .NET Standard FFMpeg/FFProbe wrapper for easily integrating media analysis and conversion into your .NET applications")]
[assembly: AssemblyFileVersion("5.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+ed8f899d04a1f71c481e02b2123176d5cd097a4a")]
[assembly: AssemblyProduct("FFMpegCore")]
[assembly: AssemblyTitle("FFMpegCore")]
[assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/rosenbjerg/FFMpegCore")]
[assembly: NeutralResourcesLanguage("en")]
[assembly: AssemblyVersion("5.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
internal sealed class EmbeddedAttribute : Attribute
{
}
}
namespace System.Runtime.CompilerServices
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
internal sealed class NullableAttribute : Attribute
{
public readonly byte[] NullableFlags;
public NullableAttribute(byte P_0)
{
NullableFlags = new byte[1] { P_0 };
}
public NullableAttribute(byte[] P_0)
{
NullableFlags = P_0;
}
}
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
internal sealed class NullableContextAttribute : Attribute
{
public readonly byte Flag;
public NullableContextAttribute(byte P_0)
{
Flag = P_0;
}
}
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
internal sealed class RefSafetyRulesAttribute : Attribute
{
public readonly int Version;
public RefSafetyRulesAttribute(int P_0)
{
Version = P_0;
}
}
}
namespace FFMpegCore
{
public class FFMetadataBuilder
{
private class FFMetadataChapter
{
public string Title { get; }
public long DurationMs { get; }
public FFMetadataChapter(string title, long durationMs)
{
Title = title;
DurationMs = durationMs;
base..ctor();
}
}
private Dictionary<string, string> Tags { get; } = new Dictionary<string, string>();
private List<FFMetadataChapter> Chapters { get; } = new List<FFMetadataChapter>();
public static FFMetadataBuilder Empty()
{
return new FFMetadataBuilder();
}
public FFMetadataBuilder WithTag(string key, string value)
{
Tags.Add(key, value);
return this;
}
public FFMetadataBuilder WithChapter(string title, long durationMs)
{
Chapters.Add(new FFMetadataChapter(title, durationMs));
return this;
}
public FFMetadataBuilder WithChapter(string title, double durationSeconds)
{
Chapters.Add(new FFMetadataChapter(title, Convert.ToInt64(durationSeconds * 1000.0)));
return this;
}
public string GetMetadataFileContent()
{
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.AppendLine(";FFMETADATA1");
foreach (KeyValuePair<string, string> tag in Tags)
{
stringBuilder.AppendLine(tag.Key + "=" + tag.Value);
}
long num = 0L;
foreach (FFMetadataChapter chapter in Chapters)
{
stringBuilder.AppendLine("[CHAPTER]");
stringBuilder.AppendLine("TIMEBASE=1/1000");
stringBuilder.AppendLine($"START={num}");
stringBuilder.AppendLine($"END={num + chapter.DurationMs}");
stringBuilder.AppendLine("title=" + chapter.Title);
num += chapter.DurationMs;
}
return stringBuilder.ToString();
}
}
public static class FFMpeg
{
public static bool Snapshot(string input, string output, Size? size = null, TimeSpan? captureTime = null, int? streamIndex = null, int inputFileIndex = 0)
{
CheckSnapshotOutputExtension(output, FileExtension.Image.All);
IMediaAnalysis source = FFProbe.Analyse(input);
return SnapshotProcess(input, output, source, size, captureTime, streamIndex, inputFileIndex).ProcessSynchronously();
}
public static async Task<bool> SnapshotAsync(string input, string output, Size? size = null, TimeSpan? captureTime = null, int? streamIndex = null, int inputFileIndex = 0, CancellationToken cancellationToken = default(CancellationToken))
{
CheckSnapshotOutputExtension(output, FileExtension.Image.All);
return await SnapshotProcess(input, output, await FFProbe.AnalyseAsync(input, null, cancellationToken).ConfigureAwait(continueOnCapturedContext: false), size, captureTime, streamIndex, inputFileIndex).CancellableThrough(cancellationToken).ProcessAsynchronously().ConfigureAwait(continueOnCapturedContext: false);
}
public static bool GifSnapshot(string input, string output, Size? size = null, TimeSpan? captureTime = null, TimeSpan? duration = null, int? streamIndex = null)
{
CheckSnapshotOutputExtension(output, new List<string>(1) { FileExtension.Gif });
IMediaAnalysis source = FFProbe.Analyse(input);
return GifSnapshotProcess(input, output, source, size, captureTime, duration, streamIndex).ProcessSynchronously();
}
public static async Task<bool> GifSnapshotAsync(string input, string output, Size? size = null, TimeSpan? captureTime = null, TimeSpan? duration = null, int? streamIndex = null, CancellationToken cancellationToken = default(CancellationToken))
{
CheckSnapshotOutputExtension(output, new List<string>(1) { FileExtension.Gif });
return await GifSnapshotProcess(input, output, await FFProbe.AnalyseAsync(input, null, cancellationToken).ConfigureAwait(continueOnCapturedContext: false), size, captureTime, duration, streamIndex).CancellableThrough(cancellationToken).ProcessAsynchronously().ConfigureAwait(continueOnCapturedContext: false);
}
private static FFMpegArgumentProcessor SnapshotProcess(string input, string output, IMediaAnalysis source, Size? size = null, TimeSpan? captureTime = null, int? streamIndex = null, int inputFileIndex = 0)
{
var (fFMpegArguments, addArguments) = SnapshotArgumentBuilder.BuildSnapshotArguments(input, output, source, size, captureTime, streamIndex, inputFileIndex);
return fFMpegArguments.OutputToFile(output, overwrite: true, addArguments);
}
private static FFMpegArgumentProcessor GifSnapshotProcess(string input, string output, IMediaAnalysis source, Size? size = null, TimeSpan? captureTime = null, TimeSpan? duration = null, int? streamIndex = null)
{
var (fFMpegArguments, addArguments) = SnapshotArgumentBuilder.BuildGifSnapshotArguments(input, source, size, captureTime, duration, streamIndex);
return fFMpegArguments.OutputToFile(output, overwrite: true, addArguments);
}
private static void CheckSnapshotOutputExtension(string output, List<string> extensions)
{
if (!extensions.Contains(Path.GetExtension(output).ToLower()))
{
throw new ArgumentException("Invalid snapshot output extension: " + output + ", needed: " + string.Join(",", FileExtension.Image.All));
}
}
public static bool JoinImageSequence(string output, double frameRate = 30.0, params string[] images)
{
string?[] array = images.Select(Path.GetExtension).Distinct().ToArray();
if (array.Length != 1)
{
throw new ArgumentException("All images must have the same extension", "images");
}
string text = array[0].ToLowerInvariant();
int? width = null;
int? height = null;
string text2 = Path.Combine(GlobalFFOptions.Current.TemporaryFilesFolder, Guid.NewGuid().ToString());
Directory.CreateDirectory(text2);
try
{
int num = 0;
foreach (string obj in images)
{
IMediaAnalysis mediaAnalysis = FFProbe.Analyse(obj);
FFMpegHelper.ConversionSizeExceptionCheck(mediaAnalysis.PrimaryVideoStream.Width, mediaAnalysis.PrimaryVideoStream.Height);
int valueOrDefault = width.GetValueOrDefault();
if (!width.HasValue)
{
valueOrDefault = mediaAnalysis.PrimaryVideoStream.Width;
width = valueOrDefault;
}
valueOrDefault = height.GetValueOrDefault();
if (!height.HasValue)
{
valueOrDefault = mediaAnalysis.PrimaryVideoStream.Height;
height = valueOrDefault;
}
string destFileName = Path.Combine(text2, num++.ToString().PadLeft(9, '0') + text);
File.Copy(obj, destFileName);
}
return FFMpegArguments.FromFileInput(Path.Combine(text2, "%09d" + text), verifyExists: false, delegate(FFMpegArgumentOptions options)
{
options.WithFramerate(frameRate);
}).OutputToFile(output, overwrite: true, delegate(FFMpegArgumentOptions options)
{
options.ForcePixelFormat("yuv420p").Resize(width.Value, height.Value).WithFramerate(frameRate);
}).ProcessSynchronously();
}
finally
{
Directory.Delete(text2, recursive: true);
}
}
public static bool PosterWithAudio(string image, string audio, string output)
{
FFMpegHelper.ExtensionExceptionCheck(output, FileExtension.Mp4);
IMediaAnalysis mediaAnalysis = FFProbe.Analyse(image);
FFMpegHelper.ConversionSizeExceptionCheck(mediaAnalysis.PrimaryVideoStream.Width, mediaAnalysis.PrimaryVideoStream.Height);
return FFMpegArguments.FromFileInput(image, verifyExists: false, delegate(FFMpegArgumentOptions options)
{
options.Loop(1).ForceFormat("image2");
}).AddFileInput(audio).OutputToFile(output, overwrite: true, delegate(FFMpegArgumentOptions options)
{
options.ForcePixelFormat("yuv420p").WithVideoCodec(VideoCodec.LibX264).WithConstantRateFactor(21)
.WithAudioBitrate(AudioQuality.Normal)
.UsingShortest();
})
.ProcessSynchronously();
}
public static bool Convert(string input, string output, ContainerFormat format, Speed speed = Speed.SuperFast, VideoSize size = VideoSize.Original, AudioQuality audioQuality = AudioQuality.Normal, bool multithreaded = false)
{
FFMpegHelper.ExtensionExceptionCheck(output, format.Extension);
IMediaAnalysis mediaAnalysis = FFProbe.Analyse(input);
FFMpegHelper.ConversionSizeExceptionCheck(mediaAnalysis);
double num = ((VideoSize.Original == size) ? 1.0 : ((double)mediaAnalysis.PrimaryVideoStream.Height / (double)size));
Size outputSize = new Size((int)((double)mediaAnalysis.PrimaryVideoStream.Width / num), (int)((double)mediaAnalysis.PrimaryVideoStream.Height / num));
if (outputSize.Width % 2 != 0)
{
outputSize.Width++;
}
return format.Name switch
{
"mp4" => FFMpegArguments.FromFileInput(input).OutputToFile(output, overwrite: true, delegate(FFMpegArgumentOptions options)
{
options.UsingMultithreading(multithreaded).WithVideoCodec(VideoCodec.LibX264).WithVideoBitrate(2400)
.WithVideoFilters(delegate(VideoFilterOptions filterOptions)
{
filterOptions.Scale(outputSize);
})
.WithSpeedPreset(speed)
.WithAudioCodec(AudioCodec.Aac)
.WithAudioBitrate(audioQuality);
}).ProcessSynchronously(),
"ogv" => FFMpegArguments.FromFileInput(input).OutputToFile(output, overwrite: true, delegate(FFMpegArgumentOptions options)
{
options.UsingMultithreading(multithreaded).WithVideoCodec(VideoCodec.LibTheora).WithVideoBitrate(2400)
.WithVideoFilters(delegate(VideoFilterOptions filterOptions)
{
filterOptions.Scale(outputSize);
})
.WithSpeedPreset(speed)
.WithAudioCodec(AudioCodec.LibVorbis)
.WithAudioBitrate(audioQuality);
}).ProcessSynchronously(),
"mpegts" => FFMpegArguments.FromFileInput(input).OutputToFile(output, overwrite: true, delegate(FFMpegArgumentOptions options)
{
options.CopyChannel().WithBitStreamFilter(Channel.Video, Filter.H264_Mp4ToAnnexB).ForceFormat(VideoType.Ts);
}).ProcessSynchronously(),
"webm" => FFMpegArguments.FromFileInput(input).OutputToFile(output, overwrite: true, delegate(FFMpegArgumentOptions options)
{
options.UsingMultithreading(multithreaded).WithVideoCodec(VideoCodec.LibVpx).WithVideoBitrate(2400)
.WithVideoFilters(delegate(VideoFilterOptions filterOptions)
{
filterOptions.Scale(outputSize);
})
.WithSpeedPreset(speed)
.WithAudioCodec(AudioCodec.LibVorbis)
.WithAudioBitrate(audioQuality);
}).ProcessSynchronously(),
_ => throw new ArgumentOutOfRangeException("format"),
};
}
public static bool Join(string output, params string[] videos)
{
string[] array = videos.Select(delegate(string videoPath)
{
FFMpegHelper.ConversionSizeExceptionCheck(FFProbe.Analyse(videoPath));
string text = Path.Combine(GlobalFFOptions.Current.TemporaryFilesFolder, Path.GetFileNameWithoutExtension(videoPath) + FileExtension.Ts);
Directory.CreateDirectory(GlobalFFOptions.Current.TemporaryFilesFolder);
Convert(videoPath, text, VideoType.Ts);
return text;
}).ToArray();
try
{
return FFMpegArguments.FromConcatInput(array).OutputToFile(output, overwrite: true, delegate(FFMpegArgumentOptions options)
{
options.CopyChannel().WithBitStreamFilter(Channel.Audio, Filter.Aac_AdtstoAsc);
}).ProcessSynchronously();
}
finally
{
Cleanup(array);
}
}
private static FFMpegArgumentProcessor BaseSubVideo(string input, string output, TimeSpan startTime, TimeSpan endTime)
{
if (Path.GetExtension(input) != Path.GetExtension(output))
{
output = Path.Combine(Path.GetDirectoryName(output), Path.GetFileNameWithoutExtension(output), Path.GetExtension(input));
}
return FFMpegArguments.FromFileInput(input, verifyExists: true, delegate(FFMpegArgumentOptions options)
{
options.Seek(startTime).EndSeek(endTime);
}).OutputToFile(output, overwrite: true, delegate(FFMpegArgumentOptions options)
{
options.CopyChannel();
});
}
public static bool SubVideo(string input, string output, TimeSpan startTime, TimeSpan endTime)
{
return BaseSubVideo(input, output, startTime, endTime).ProcessSynchronously();
}
public static async Task<bool> SubVideoAsync(string input, string output, TimeSpan startTime, TimeSpan endTime, CancellationToken cancellationToken = default(CancellationToken))
{
return await BaseSubVideo(input, output, startTime, endTime).CancellableThrough(cancellationToken).ProcessAsynchronously().ConfigureAwait(continueOnCapturedContext: false);
}
public static bool SaveM3U8Stream(Uri uri, string output)
{
FFMpegHelper.ExtensionExceptionCheck(output, FileExtension.Mp4);
if (uri.Scheme != "http" && uri.Scheme != "https")
{
throw new ArgumentException("Uri: " + uri.AbsoluteUri + ", does not point to a valid http(s) stream.");
}
return FFMpegArguments.FromUrlInput(uri, delegate(FFMpegArgumentOptions options)
{
options.WithCopyCodec();
}).OutputToFile(output).ProcessSynchronously();
}
public static bool Mute(string input, string output)
{
FFMpegHelper.ConversionSizeExceptionCheck(FFProbe.Analyse(input));
return FFMpegArguments.FromFileInput(input).OutputToFile(output, overwrite: true, delegate(FFMpegArgumentOptions options)
{
options.CopyChannel(Channel.Video).DisableChannel(Channel.Audio);
}).ProcessSynchronously();
}
public static bool ExtractAudio(string input, string output)
{
FFMpegHelper.ExtensionExceptionCheck(output, FileExtension.Mp3);
return FFMpegArguments.FromFileInput(input).OutputToFile(output, overwrite: true, delegate(FFMpegArgumentOptions options)
{
options.DisableChannel(Channel.Video);
}).ProcessSynchronously();
}
public static bool ReplaceAudio(string input, string inputAudio, string output, bool stopAtShortest = false)
{
FFMpegHelper.ConversionSizeExceptionCheck(FFProbe.Analyse(input));
return FFMpegArguments.FromFileInput(input).AddFileInput(inputAudio).OutputToFile(output, overwrite: true, delegate(FFMpegArgumentOptions options)
{
options.CopyChannel().WithAudioCodec(AudioCodec.Aac).WithAudioBitrate(AudioQuality.Good)
.UsingShortest(stopAtShortest);
})
.ProcessSynchronously();
}
private static void Cleanup(IEnumerable<string> pathList)
{
foreach (string path in pathList)
{
if (File.Exists(path))
{
File.Delete(path);
}
}
}
internal static IReadOnlyList<PixelFormat> GetPixelFormatsInternal()
{
//IL_0021: Unknown result type (might be due to invalid IL or missing references)
//IL_0026: Unknown result type (might be due to invalid IL or missing references)
//IL_003d: Expected O, but got Unknown
FFMpegHelper.RootExceptionCheck();
List<PixelFormat> list = new List<PixelFormat>();
ProcessArguments val = new ProcessArguments(GlobalFFOptions.GetFFMpegBinaryPath(), "-pix_fmts");
val.OutputDataReceived += delegate(object e, string data)
{
if (PixelFormat.TryParse(data, out PixelFormat fmt))
{
list.Add(fmt);
}
};
IProcessResult val2 = ProcessArgumentsExtensions.StartAndWaitForExit(val);
if (val2.ExitCode != 0)
{
throw new FFMpegException(FFMpegExceptionType.Process, string.Join("\r\n", val2.OutputData));
}
return list.AsReadOnly();
}
public static IReadOnlyList<PixelFormat> GetPixelFormats()
{
if (!GlobalFFOptions.Current.UseCache)
{
return GetPixelFormatsInternal();
}
return FFMpegCache.PixelFormats.Values.ToList().AsReadOnly();
}
public static bool TryGetPixelFormat(string name, out PixelFormat format)
{
string name2 = name;
if (!GlobalFFOptions.Current.UseCache)
{
format = GetPixelFormatsInternal().FirstOrDefault((PixelFormat x) => x.Name == name2.ToLowerInvariant().Trim());
return format != null;
}
return FFMpegCache.PixelFormats.TryGetValue(name2, out format);
}
public static PixelFormat GetPixelFormat(string name)
{
if (TryGetPixelFormat(name, out PixelFormat format))
{
return format;
}
throw new FFMpegException(FFMpegExceptionType.Operation, "Pixel format \"" + name + "\" not supported");
}
private static void ParsePartOfCodecs(Dictionary<string, Codec> codecs, string arguments, Func<string, Codec?> parser)
{
//IL_0020: Unknown result type (might be due to invalid IL or missing references)
//IL_0025: Unknown result type (might be due to invalid IL or missing references)
//IL_003c: Expected O, but got Unknown
Func<string, Codec?> parser2 = parser;
Dictionary<string, Codec> codecs2 = codecs;
FFMpegHelper.RootExceptionCheck();
ProcessArguments val = new ProcessArguments(GlobalFFOptions.GetFFMpegBinaryPath(), arguments);
val.OutputDataReceived += delegate(object e, string data)
{
Codec codec = parser2(data);
if (codec != null)
{
if (codecs2.TryGetValue(codec.Name, out Codec value))
{
value.Merge(codec);
}
else
{
codecs2.Add(codec.Name, codec);
}
}
};
IProcessResult val2 = ProcessArgumentsExtensions.StartAndWaitForExit(val);
if (val2.ExitCode != 0)
{
throw new FFMpegException(FFMpegExceptionType.Process, string.Join("\r\n", val2.OutputData));
}
}
internal static Dictionary<string, Codec> GetCodecsInternal()
{
Dictionary<string, Codec> dictionary = new Dictionary<string, Codec>();
ParsePartOfCodecs(dictionary, "-codecs", (string s) => Codec.TryParseFromCodecs(s, out Codec codec3) ? codec3 : null);
ParsePartOfCodecs(dictionary, "-encoders", (string s) => Codec.TryParseFromEncodersDecoders(s, out Codec codec2, isEncoder: true) ? codec2 : null);
ParsePartOfCodecs(dictionary, "-decoders", (string s) => Codec.TryParseFromEncodersDecoders(s, out Codec codec, isEncoder: false) ? codec : null);
return dictionary;
}
public static IReadOnlyList<Codec> GetCodecs()
{
if (!GlobalFFOptions.Current.UseCache)
{
return GetCodecsInternal().Values.ToList().AsReadOnly();
}
return FFMpegCache.Codecs.Values.ToList().AsReadOnly();
}
public static IReadOnlyList<Codec> GetCodecs(CodecType type)
{
if (!GlobalFFOptions.Current.UseCache)
{
return GetCodecsInternal().Values.Where((Codec x) => x.Type == type).ToList().AsReadOnly();
}
return FFMpegCache.Codecs.Values.Where((Codec x) => x.Type == type).ToList().AsReadOnly();
}
public static IReadOnlyList<Codec> GetVideoCodecs()
{
return GetCodecs(CodecType.Video);
}
public static IReadOnlyList<Codec> GetAudioCodecs()
{
return GetCodecs(CodecType.Audio);
}
public static IReadOnlyList<Codec> GetSubtitleCodecs()
{
return GetCodecs(CodecType.Subtitle);
}
public static IReadOnlyList<Codec> GetDataCodecs()
{
return GetCodecs(CodecType.Data);
}
public static bool TryGetCodec(string name, out Codec codec)
{
string name2 = name;
if (!GlobalFFOptions.Current.UseCache)
{
codec = GetCodecsInternal().Values.FirstOrDefault((Codec x) => x.Name == name2.ToLowerInvariant().Trim());
return codec != null;
}
return FFMpegCache.Codecs.TryGetValue(name2, out codec);
}
public static Codec GetCodec(string name)
{
if (TryGetCodec(name, out Codec codec) && codec != null)
{
return codec;
}
throw new FFMpegException(FFMpegExceptionType.Operation, "Codec \"" + name + "\" not supported");
}
internal static IReadOnlyList<ContainerFormat> GetContainersFormatsInternal()
{
//IL_0021: Unknown result type (might be due to invalid IL or missing references)
//IL_0026: Unknown result type (might be due to invalid IL or missing references)
//IL_003d: Expected O, but got Unknown
FFMpegHelper.RootExceptionCheck();
List<ContainerFormat> list = new List<ContainerFormat>();
ProcessArguments val = new ProcessArguments(GlobalFFOptions.GetFFMpegBinaryPath(), "-formats");
val.OutputDataReceived += delegate(object e, string data)
{
if (ContainerFormat.TryParse(data, out ContainerFormat fmt))
{
list.Add(fmt);
}
};
IProcessResult val2 = ProcessArgumentsExtensions.StartAndWaitForExit(val);
if (val2.ExitCode != 0)
{
throw new FFMpegException(FFMpegExceptionType.Process, string.Join("\r\n", val2.OutputData));
}
return list.AsReadOnly();
}
public static IReadOnlyList<ContainerFormat> GetContainerFormats()
{
if (!GlobalFFOptions.Current.UseCache)
{
return GetContainersFormatsInternal();
}
return FFMpegCache.ContainerFormats.Values.ToList().AsReadOnly();
}
public static bool TryGetContainerFormat(string name, out ContainerFormat fmt)
{
string name2 = name;
if (!GlobalFFOptions.Current.UseCache)
{
fmt = GetContainersFormatsInternal().FirstOrDefault((ContainerFormat x) => x.Name == name2.ToLowerInvariant().Trim());
return fmt != null;
}
return FFMpegCache.ContainerFormats.TryGetValue(name2, out fmt);
}
public static ContainerFormat GetContainerFormat(string name)
{
if (TryGetContainerFormat(name, out ContainerFormat fmt))
{
return fmt;
}
throw new FFMpegException(FFMpegExceptionType.Operation, "Container format \"" + name + "\" not supported");
}
}
public class FFMpegArgumentOptions : FFMpegArgumentsBase
{
internal FFMpegArgumentOptions()
{
}
public FFMpegArgumentOptions WithAudioCodec(Codec audioCodec)
{
return WithArgument(new AudioCodecArgument(audioCodec));
}
public FFMpegArgumentOptions WithAudioCodec(string audioCodec)
{
return WithArgument(new AudioCodecArgument(audioCodec));
}
public FFMpegArgumentOptions WithAudioBitrate(AudioQuality audioQuality)
{
return WithArgument(new AudioBitrateArgument(audioQuality));
}
public FFMpegArgumentOptions WithAudioBitrate(int bitrate)
{
return WithArgument(new AudioBitrateArgument(bitrate));
}
public FFMpegArgumentOptions WithAudioSamplingRate(int samplingRate = 48000)
{
return WithArgument(new AudioSamplingRateArgument(samplingRate));
}
public FFMpegArgumentOptions WithVariableBitrate(int vbr)
{
return WithArgument(new VariableBitRateArgument(vbr));
}
public FFMpegArgumentOptions Resize(int width, int height)
{
return WithArgument(new SizeArgument(width, height));
}
public FFMpegArgumentOptions Resize(Size? size)
{
return WithArgument(new SizeArgument(size));
}
public FFMpegArgumentOptions Crop(Size? size, int left, int top)
{
return WithArgument(new CropArgument(size, top, left));
}
public FFMpegArgumentOptions Crop(int width, int height, int left, int top)
{
return WithArgument(new CropArgument(new Size(width, height), top, left));
}
public FFMpegArgumentOptions Crop(Size? size)
{
return WithArgument(new CropArgument(size, 0, 0));
}
public FFMpegArgumentOptions Crop(int width, int height)
{
return WithArgument(new CropArgument(new Size(width, height), 0, 0));
}
public FFMpegArgumentOptions WithBitStreamFilter(Channel channel, Filter filter)
{
return WithArgument(new BitStreamFilterArgument(channel, filter));
}
public FFMpegArgumentOptions WithConstantRateFactor(int crf)
{
return WithArgument(new ConstantRateFactorArgument(crf));
}
public FFMpegArgumentOptions CopyChannel(Channel channel = Channel.Both)
{
return WithArgument(new CopyArgument(channel));
}
public FFMpegArgumentOptions DisableChannel(Channel channel)
{
return WithArgument(new DisableChannelArgument(channel));
}
public FFMpegArgumentOptions WithDuration(TimeSpan? duration)
{
return WithArgument(new DurationArgument(duration));
}
public FFMpegArgumentOptions WithFastStart()
{
return WithArgument(new FaststartArgument());
}
public FFMpegArgumentOptions WithFrameOutputCount(int frames)
{
return WithArgument(new FrameOutputCountArgument(frames));
}
public FFMpegArgumentOptions WithHardwareAcceleration(HardwareAccelerationDevice hardwareAccelerationDevice = HardwareAccelerationDevice.Auto)
{
return WithArgument(new HardwareAccelerationArgument(hardwareAccelerationDevice));
}
public FFMpegArgumentOptions UsingShortest(bool shortest = true)
{
return WithArgument(new ShortestArgument(shortest));
}
public FFMpegArgumentOptions UsingMultithreading(bool multithread)
{
return WithArgument(new ThreadsArgument(multithread));
}
public FFMpegArgumentOptions UsingThreads(int threads)
{
return WithArgument(new ThreadsArgument(threads));
}
public FFMpegArgumentOptions WithVideoCodec(Codec videoCodec)
{
return WithArgument(new VideoCodecArgument(videoCodec));
}
public FFMpegArgumentOptions WithVideoCodec(string videoCodec)
{
return WithArgument(new VideoCodecArgument(videoCodec));
}
public FFMpegArgumentOptions WithVideoBitrate(int bitrate)
{
return WithArgument(new VideoBitrateArgument(bitrate));
}
public FFMpegArgumentOptions WithVideoFilters(Action<VideoFilterOptions> videoFilterOptions)
{
VideoFilterOptions videoFilterOptions2 = new VideoFilterOptions();
videoFilterOptions(videoFilterOptions2);
return WithArgument(new VideoFiltersArgument(videoFilterOptions2));
}
public FFMpegArgumentOptions WithAudioFilters(Action<AudioFilterOptions> audioFilterOptions)
{
AudioFilterOptions audioFilterOptions2 = new AudioFilterOptions();
audioFilterOptions(audioFilterOptions2);
return WithArgument(new AudioFiltersArgument(audioFilterOptions2));
}
public FFMpegArgumentOptions WithFramerate(double framerate)
{
return WithArgument(new FrameRateArgument(framerate));
}
public FFMpegArgumentOptions WithoutMetadata()
{
return WithArgument(new RemoveMetadataArgument());
}
public FFMpegArgumentOptions WithSpeedPreset(Speed speed)
{
return WithArgument(new SpeedPresetArgument(speed));
}
public FFMpegArgumentOptions WithStartNumber(int startNumber)
{
return WithArgument(new StartNumberArgument(startNumber));
}
public FFMpegArgumentOptions WithCustomArgument(string argument)
{
return WithArgument(new CustomArgument(argument));
}
public FFMpegArgumentOptions Seek(TimeSpan? seekTo)
{
return WithArgument(new SeekArgument(seekTo));
}
public FFMpegArgumentOptions EndSeek(TimeSpan? seekTo)
{
return WithArgument(new EndSeekArgument(seekTo));
}
public FFMpegArgumentOptions Loop(int times)
{
return WithArgument(new LoopArgument(times));
}
public FFMpegArgumentOptions OverwriteExisting()
{
return WithArgument(new OverwriteArgument());
}
public FFMpegArgumentOptions SelectStream(int streamIndex, int inputFileIndex = 0, Channel channel = Channel.All)
{
return WithArgument(new MapStreamArgument(streamIndex, inputFileIndex, channel));
}
public FFMpegArgumentOptions SelectStreams(IEnumerable<int> streamIndices, int inputFileIndex = 0, Channel channel = Channel.All)
{
return streamIndices.Aggregate(this, (FFMpegArgumentOptions options, int streamIndex) => options.SelectStream(streamIndex, inputFileIndex, channel));
}
public FFMpegArgumentOptions DeselectStream(int streamIndex, int inputFileIndex = 0, Channel channel = Channel.All)
{
return WithArgument(new MapStreamArgument(streamIndex, inputFileIndex, channel, negativeMap: true));
}
public FFMpegArgumentOptions DeselectStreams(IEnumerable<int> streamIndices, int inputFileIndex = 0, Channel channel = Channel.All)
{
return streamIndices.Aggregate(this, (FFMpegArgumentOptions options, int streamIndex) => options.DeselectStream(streamIndex, inputFileIndex, channel));
}
public FFMpegArgumentOptions ForceFormat(ContainerFormat format)
{
return WithArgument(new ForceFormatArgument(format));
}
public FFMpegArgumentOptions ForceFormat(string format)
{
return WithArgument(new ForceFormatArgument(format));
}
public FFMpegArgumentOptions ForcePixelFormat(string pixelFormat)
{
return WithArgument(new ForcePixelFormat(pixelFormat));
}
public FFMpegArgumentOptions ForcePixelFormat(PixelFormat pixelFormat)
{
return WithArgument(new ForcePixelFormat(pixelFormat));
}
public FFMpegArgumentOptions WithAudibleEncryptionKeys(string key, string iv)
{
return WithArgument(new AudibleEncryptionKeyArgument(key, iv));
}
public FFMpegArgumentOptions WithAudibleActivationBytes(string activationBytes)
{
return WithArgument(new AudibleEncryptionKeyArgument(activationBytes));
}
public FFMpegArgumentOptions WithTagVersion(int id3v2Version = 3)
{
return WithArgument(new ID3V2VersionArgument(id3v2Version));
}
public FFMpegArgumentOptions WithGifPaletteArgument(int streamIndex, Size? size, double fps = 12.0)
{
return WithArgument(new GifPaletteArgument(streamIndex, fps, size));
}
public FFMpegArgumentOptions WithCopyCodec()
{
return WithArgument(new CopyCodecArgument());
}
public FFMpegArgumentOptions WithArgument(IArgument argument)
{
Arguments.Add(argument);
return this;
}
}
public class FFMpegArgumentProcessor
{
private static readonly Regex ProgressRegex = new Regex("time=(\\d\\d:\\d\\d:\\d\\d.\\d\\d?)", RegexOptions.Compiled);
private readonly List<Action<FFOptions>> _configurations;
private readonly FFMpegArguments _ffMpegArguments;
private CancellationTokenRegistration? _cancellationTokenRegistration;
private bool _cancelled;
private FFMpegLogLevel? _logLevel;
private Action<string>? _onError;
private Action<string>? _onOutput;
private Action<double>? _onPercentageProgress;
private Action<TimeSpan>? _onTimeProgress;
private TimeSpan? _totalTimespan;
public string Arguments => _ffMpegArguments.Text;
private event EventHandler<int> CancelEvent;
internal FFMpegArgumentProcessor(FFMpegArguments ffMpegArguments)
{
_configurations = new List<Action<FFOptions>>();
_ffMpegArguments = ffMpegArguments;
}
public FFMpegArgumentProcessor NotifyOnProgress(Action<double> onPercentageProgress, TimeSpan totalTimeSpan)
{
_totalTimespan = totalTimeSpan;
_onPercentageProgress = onPercentageProgress;
return this;
}
public FFMpegArgumentProcessor NotifyOnProgress(Action<TimeSpan> onTimeProgress)
{
_onTimeProgress = onTimeProgress;
return this;
}
public FFMpegArgumentProcessor NotifyOnOutput(Action<string> onOutput)
{
_onOutput = onOutput;
return this;
}
public FFMpegArgumentProcessor NotifyOnError(Action<string> onError)
{
_onError = onError;
return this;
}
private void Cancel(int timeout)
{
_cancelled = true;
this.CancelEvent?.Invoke(this, timeout);
}
public FFMpegArgumentProcessor CancellableThrough(out Action cancel, int timeout = 0)
{
cancel = delegate
{
Cancel(timeout);
};
return this;
}
public FFMpegArgumentProcessor CancellableThrough(CancellationToken token, int timeout = 0)
{
_cancellationTokenRegistration?.Dispose();
_cancellationTokenRegistration = token.Register(delegate
{
Cancel(timeout);
});
return this;
}
public FFMpegArgumentProcessor Configure(Action<FFOptions> configureOptions)
{
_configurations.Add(configureOptions);
return this;
}
public FFMpegArgumentProcessor WithLogLevel(FFMpegLogLevel logLevel)
{
_logLevel = logLevel;
return this;
}
public bool ProcessSynchronously(bool throwOnError = true, FFOptions? ffMpegOptions = null)
{
FFOptions configuredOptions = GetConfiguredOptions(ffMpegOptions);
ProcessArguments processArguments = PrepareProcessArguments(configuredOptions);
using CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
IProcessResult val = null;
try
{
val = Process(processArguments, cancellationTokenSource).ConfigureAwait(continueOnCapturedContext: false).GetAwaiter().GetResult();
}
catch (OperationCanceledException)
{
if (throwOnError)
{
throw;
}
}
return HandleCompletion(throwOnError, (val != null) ? val.ExitCode : (-1), ((val != null) ? val.ErrorData : null) ?? Array.Empty<string>());
}
public async Task<bool> ProcessAsynchronously(bool throwOnError = true, FFOptions? ffMpegOptions = null)
{
FFOptions configuredOptions = GetConfiguredOptions(ffMpegOptions);
ProcessArguments processArguments = PrepareProcessArguments(configuredOptions);
using CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
IProcessResult processResult = null;
try
{
processResult = await Process(processArguments, cancellationTokenSource).ConfigureAwait(continueOnCapturedContext: false);
}
catch (OperationCanceledException)
{
if (throwOnError)
{
throw;
}
}
FFMpegArgumentProcessor fFMpegArgumentProcessor = this;
IProcessResult obj = processResult;
int exitCode = ((obj != null) ? obj.ExitCode : (-1));
IProcessResult obj2 = processResult;
return fFMpegArgumentProcessor.HandleCompletion(throwOnError, exitCode, ((obj2 != null) ? obj2.ErrorData : null) ?? Array.Empty<string>());
}
private async Task<IProcessResult> Process(ProcessArguments processArguments, CancellationTokenSource cancellationTokenSource)
{
CancellationTokenSource cancellationTokenSource2 = cancellationTokenSource;
IProcessResult processResult = null;
if (_cancelled)
{
_cancellationTokenRegistration?.Dispose();
throw new OperationCanceledException("cancelled before starting processing");
}
_ffMpegArguments.Pre();
IProcessInstance instance = processArguments.Start();
try
{
CancelEvent += OnCancelEvent;
try
{
await Task.WhenAll(instance.WaitForExitAsync(default(CancellationToken)).ContinueWith(delegate(Task<IProcessResult> t)
{
processResult = t.Result;
cancellationTokenSource2.Cancel();
_ffMpegArguments.Post();
}), _ffMpegArguments.During(cancellationTokenSource2.Token)).ConfigureAwait(continueOnCapturedContext: false);
if (_cancelled)
{
_cancellationTokenRegistration?.Dispose();
throw new OperationCanceledException("ffmpeg processing was cancelled");
}
return processResult;
}
finally
{
CancelEvent -= OnCancelEvent;
_cancellationTokenRegistration?.Dispose();
}
}
finally
{
if (instance != null)
{
((IDisposable)instance).Dispose();
}
}
static void ExecuteIgnoringFinishedProcessExceptions(Action action)
{
try
{
action();
}
catch (InstanceProcessAlreadyExitedException)
{
}
catch (ObjectDisposedException)
{
}
}
void OnCancelEvent(object sender, int timeout)
{
ExecuteIgnoringFinishedProcessExceptions(delegate
{
instance.SendInput("q");
});
if (!cancellationTokenSource2.Token.WaitHandle.WaitOne(timeout, exitContext: true))
{
cancellationTokenSource2.Cancel();
ExecuteIgnoringFinishedProcessExceptions(delegate
{
instance.Kill();
});
}
}
}
private bool HandleCompletion(bool throwOnError, int exitCode, IReadOnlyList<string> errorData)
{
if (throwOnError && exitCode != 0)
{
throw new FFMpegException(FFMpegExceptionType.Process, string.Format("ffmpeg exited with non-zero exit-code ({0} - {1})", exitCode, string.Join("\n", errorData)), null, string.Join("\n", errorData));
}
_onPercentageProgress?.Invoke(100.0);
if (_totalTimespan.HasValue)
{
_onTimeProgress?.Invoke(_totalTimespan.Value);
}
return exitCode == 0;
}
internal FFOptions GetConfiguredOptions(FFOptions? ffOptions)
{
FFOptions fFOptions = ffOptions ?? GlobalFFOptions.Current.Clone();
foreach (Action<FFOptions> configuration in _configurations)
{
configuration(fFOptions);
}
return fFOptions;
}
private ProcessArguments PrepareProcessArguments(FFOptions ffOptions)
{
//IL_009d: Unknown result type (might be due to invalid IL or missing references)
//IL_00a3: Expected O, but got Unknown
FFMpegHelper.RootExceptionCheck();
FFMpegHelper.VerifyFFMpegExists(ffOptions);
string text = _ffMpegArguments.Text;
if (!_logLevel.HasValue)
{
_logLevel = ffOptions.LogLevel;
}
if (_logLevel.HasValue)
{
string text2 = _logLevel.ToString().ToLower();
text = text + " -v " + text2;
}
ProcessArguments val = new ProcessArguments(new ProcessStartInfo
{
FileName = GlobalFFOptions.GetFFMpegBinaryPath(ffOptions),
Arguments = text,
StandardOutputEncoding = ffOptions.Encoding,
StandardErrorEncoding = ffOptions.Encoding,
WorkingDirectory = ffOptions.WorkingDirectory
});
if (_onOutput != null)
{
val.OutputDataReceived += OutputData;
}
if (_onError != null || _onTimeProgress != null || (_onPercentageProgress != null && _totalTimespan.HasValue))
{
val.ErrorDataReceived += ErrorData;
}
return val;
}
private void ErrorData(object sender, string msg)
{
_onError?.Invoke(msg);
Match match = ProgressRegex.Match(msg);
if (match.Success)
{
TimeSpan obj = MediaAnalysisUtils.ParseDuration(match.Groups[1].Value);
_onTimeProgress?.Invoke(obj);
if (_onPercentageProgress != null && _totalTimespan.HasValue)
{
double obj2 = Math.Round(obj.TotalSeconds / _totalTimespan.Value.TotalSeconds * 100.0, 2);
_onPercentageProgress(obj2);
}
}
}
private void OutputData(object sender, string msg)
{
_onOutput?.Invoke(msg);
}
}
public sealed class FFMpegArguments : FFMpegArgumentsBase
{
private readonly FFMpegGlobalArguments _globalArguments = new FFMpegGlobalArguments();
public string Text => GetText();
private FFMpegArguments()
{
}
private string GetText()
{
IArgument[] allArguments = _globalArguments.Arguments.Concat(Arguments).ToArray();
return string.Join(" ", allArguments.Select((IArgument arg) => (!(arg is IDynamicArgument dynamicArgument)) ? arg.Text : dynamicArgument.GetText(allArguments)));
}
public static FFMpegArguments FromConcatInput(IEnumerable<string> filePaths, Action<FFMpegArgumentOptions>? addArguments = null)
{
return new FFMpegArguments().WithInput(new ConcatArgument(filePaths), addArguments);
}
public static FFMpegArguments FromDemuxConcatInput(IEnumerable<string> filePaths, Action<FFMpegArgumentOptions>? addArguments = null)
{
return new FFMpegArguments().WithInput(new DemuxConcatArgument(filePaths), addArguments);
}
public static FFMpegArguments FromFileInput(string filePath, bool verifyExists = true, Action<FFMpegArgumentOptions>? addArguments = null)
{
return new FFMpegArguments().WithInput(new InputArgument(verifyExists, filePath), addArguments);
}
public static FFMpegArguments FromFileInput(IEnumerable<string> filePath, bool verifyExists = true, Action<FFMpegArgumentOptions>? addArguments = null)
{
return new FFMpegArguments().WithInput(new MultiInputArgument(verifyExists, filePath), addArguments);
}
public static FFMpegArguments FromFileInput(FileInfo fileInfo, Action<FFMpegArgumentOptions>? addArguments = null)
{
return new FFMpegArguments().WithInput(new InputArgument(fileInfo.FullName, verifyExists: false), addArguments);
}
public static FFMpegArguments FromUrlInput(Uri uri, Action<FFMpegArgumentOptions>? addArguments = null)
{
return new FFMpegArguments().WithInput(new InputArgument(uri.AbsoluteUri, verifyExists: false), addArguments);
}
public static FFMpegArguments FromDeviceInput(string device, Action<FFMpegArgumentOptions>? addArguments = null)
{
return new FFMpegArguments().WithInput(new InputDeviceArgument(device), addArguments);
}
public static FFMpegArguments FromPipeInput(IPipeSource sourcePipe, Action<FFMpegArgumentOptions>? addArguments = null)
{
return new FFMpegArguments().WithInput(new InputPipeArgument(sourcePipe), addArguments);
}
public FFMpegArguments WithGlobalOptions(Action<FFMpegGlobalArguments> configureOptions)
{
configureOptions(_globalArguments);
return this;
}
public FFMpegArguments AddConcatInput(IEnumerable<string> filePaths, Action<FFMpegArgumentOptions>? addArguments = null)
{
return WithInput(new ConcatArgument(filePaths), addArguments);
}
public FFMpegArguments AddDemuxConcatInput(IEnumerable<string> filePaths, Action<FFMpegArgumentOptions>? addArguments = null)
{
return WithInput(new DemuxConcatArgument(filePaths), addArguments);
}
public FFMpegArguments AddFileInput(string filePath, bool verifyExists = true, Action<FFMpegArgumentOptions>? addArguments = null)
{
return WithInput(new InputArgument(verifyExists, filePath), addArguments);
}
public FFMpegArguments AddFileInput(IEnumerable<string> filePath, bool verifyExists = true, Action<FFMpegArgumentOptions>? addArguments = null)
{
return WithInput(new MultiInputArgument(verifyExists, filePath), addArguments);
}
public FFMpegArguments AddFileInput(FileInfo fileInfo, Action<FFMpegArgumentOptions>? addArguments = null)
{
return WithInput(new InputArgument(fileInfo.FullName, verifyExists: false), addArguments);
}
public FFMpegArguments AddUrlInput(Uri uri, Action<FFMpegArgumentOptions>? addArguments = null)
{
return WithInput(new InputArgument(uri.AbsoluteUri, verifyExists: false), addArguments);
}
public FFMpegArguments AddDeviceInput(string device, Action<FFMpegArgumentOptions>? addArguments = null)
{
return WithInput(new InputDeviceArgument(device), addArguments);
}
public FFMpegArguments AddPipeInput(IPipeSource sourcePipe, Action<FFMpegArgumentOptions>? addArguments = null)
{
return WithInput(new InputPipeArgument(sourcePipe), addArguments);
}
public FFMpegArguments AddMetaData(string content, Action<FFMpegArgumentOptions>? addArguments = null)
{
return WithInput(new MetaDataArgument(content), addArguments);
}
public FFMpegArguments AddMetaData(FFMetadataBuilder metaDataBuilder, Action<FFMpegArgumentOptions>? addArguments = null)
{
return WithInput(new MetaDataArgument(metaDataBuilder.GetMetadataFileContent()), addArguments);
}
public FFMpegArguments AddMetaData(IReadOnlyMetaData metaData, Action<FFMpegArgumentOptions>? addArguments = null)
{
return WithInput(new MetaDataArgument(MetaDataSerializer.Instance.Serialize(metaData)), addArguments);
}
public FFMpegArguments MapMetaData(int? inputIndex = null, Action<FFMpegArgumentOptions>? addArguments = null)
{
return WithInput(new MapMetadataArgument(inputIndex), addArguments);
}
private FFMpegArguments WithInput(IInputArgument inputArgument, Action<FFMpegArgumentOptions>? addArguments)
{
FFMpegArgumentOptions fFMpegArgumentOptions = new FFMpegArgumentOptions();
addArguments?.Invoke(fFMpegArgumentOptions);
Arguments.AddRange(fFMpegArgumentOptions.Arguments);
Arguments.Add(inputArgument);
return this;
}
public FFMpegArgumentProcessor OutputToFile(string file, bool overwrite = true, Action<FFMpegArgumentOptions>? addArguments = null)
{
return ToProcessor(new OutputArgument(file, overwrite), addArguments);
}
public FFMpegArgumentProcessor OutputToUrl(string uri, Action<FFMpegArgumentOptions>? addArguments = null)
{
return ToProcessor(new OutputUrlArgument(uri), addArguments);
}
public FFMpegArgumentProcessor OutputToUrl(Uri uri, Action<FFMpegArgumentOptions>? addArguments = null)
{
return ToProcessor(new OutputUrlArgument(uri.ToString()), addArguments);
}
public FFMpegArgumentProcessor OutputToPipe(IPipeSink reader, Action<FFMpegArgumentOptions>? addArguments = null)
{
return ToProcessor(new OutputPipeArgument(reader), addArguments);
}
private FFMpegArgumentProcessor ToProcessor(IOutputArgument argument, Action<FFMpegArgumentOptions>? addArguments)
{
FFMpegArgumentOptions fFMpegArgumentOptions = new FFMpegArgumentOptions();
addArguments?.Invoke(fFMpegArgumentOptions);
Arguments.AddRange(fFMpegArgumentOptions.Arguments);
Arguments.Add(argument);
return new FFMpegArgumentProcessor(this);
}
public FFMpegArgumentProcessor OutputToTee(Action<FFMpegMultiOutputOptions> addOutputs, Action<FFMpegArgumentOptions>? addArguments = null)
{
FFMpegMultiOutputOptions fFMpegMultiOutputOptions = new FFMpegMultiOutputOptions();
addOutputs(fFMpegMultiOutputOptions);
return ToProcessor(new OutputTeeArgument(fFMpegMultiOutputOptions), addArguments);
}
public FFMpegArgumentProcessor MultiOutput(Action<FFMpegMultiOutputOptions> addOutputs)
{
FFMpegMultiOutputOptions fFMpegMultiOutputOptions = new FFMpegMultiOutputOptions();
addOutputs(fFMpegMultiOutputOptions);
Arguments.AddRange(fFMpegMultiOutputOptions.Arguments);
return new FFMpegArgumentProcessor(this);
}
internal void Pre()
{
foreach (IInputOutputArgument item in Arguments.OfType<IInputOutputArgument>())
{
item.Pre();
}
}
internal async Task During(CancellationToken cancellationToken = default(CancellationToken))
{
await Task.WhenAll(from io in Arguments.OfType<IInputOutputArgument>()
select io.During(cancellationToken)).ConfigureAwait(continueOnCapturedContext: false);
}
internal void Post()
{
foreach (IInputOutputArgument item in Arguments.OfType<IInputOutputArgument>())
{
item.Post();
}
}
}
public abstract class FFMpegArgumentsBase
{
internal readonly List<IArgument> Arguments = new List<IArgument>();
}
internal static class FFMpegCache
{
private static readonly object _syncObject = new object();
private static Dictionary<string, PixelFormat>? _pixelFormats;
private static Dictionary<string, Codec>? _codecs;
private static Dictionary<string, ContainerFormat>? _containers;
public static IReadOnlyDictionary<string, PixelFormat> PixelFormats
{
get
{
if (_pixelFormats == null)
{
lock (_syncObject)
{
if (_pixelFormats == null)
{
_pixelFormats = FFMpeg.GetPixelFormatsInternal().ToDictionary((PixelFormat x) => x.Name);
}
}
}
return _pixelFormats;
}
}
public static IReadOnlyDictionary<string, Codec> Codecs
{
get
{
if (_codecs == null)
{
lock (_syncObject)
{
if (_codecs == null)
{
_codecs = FFMpeg.GetCodecsInternal();
}
}
}
return _codecs;
}
}
public static IReadOnlyDictionary<string, ContainerFormat> ContainerFormats
{
get
{
if (_containers == null)
{
lock (_syncObject)
{
if (_containers == null)
{
_containers = FFMpeg.GetContainersFormatsInternal().ToDictionary((ContainerFormat x) => x.Name);
}
}
}
return _containers;
}
}
}
public sealed class FFMpegGlobalArguments : FFMpegArgumentsBase
{
internal FFMpegGlobalArguments()
{
}
public FFMpegGlobalArguments WithVerbosityLevel(VerbosityLevel verbosityLevel = VerbosityLevel.Error)
{
return WithOption(new VerbosityLevelArgument(verbosityLevel));
}
private FFMpegGlobalArguments WithOption(IArgument argument)
{
Arguments.Add(argument);
return this;
}
}
public class FFMpegMultiOutputOptions
{
internal readonly List<FFMpegArgumentOptions> Outputs = new List<FFMpegArgumentOptions>();
public IEnumerable<IArgument> Arguments => Outputs.SelectMany((FFMpegArgumentOptions o) => o.Arguments);
public FFMpegMultiOutputOptions OutputToFile(string file, bool overwrite = true, Action<FFMpegArgumentOptions>? addArguments = null)
{
return AddOutput(new OutputArgument(file, overwrite), addArguments);
}
public FFMpegMultiOutputOptions OutputToUrl(string uri, Action<FFMpegArgumentOptions>? addArguments = null)
{
return AddOutput(new OutputUrlArgument(uri), addArguments);
}
public FFMpegMultiOutputOptions OutputToUrl(Uri uri, Action<FFMpegArgumentOptions>? addArguments = null)
{
return AddOutput(new OutputUrlArgument(uri.ToString()), addArguments);
}
public FFMpegMultiOutputOptions OutputToPipe(IPipeSink reader, Action<FFMpegArgumentOptions>? addArguments = null)
{
return AddOutput(new OutputPipeArgument(reader), addArguments);
}
public FFMpegMultiOutputOptions AddOutput(IOutputArgument argument, Action<FFMpegArgumentOptions>? addArguments)
{
FFMpegArgumentOptions fFMpegArgumentOptions = new FFMpegArgumentOptions();
addArguments?.Invoke(fFMpegArgumentOptions);
fFMpegArgumentOptions.Arguments.Add(argument);
Outputs.Add(fFMpegArgumentOptions);
return this;
}
}
public static class SnapshotArgumentBuilder
{
public static (FFMpegArguments, Action<FFMpegArgumentOptions> outputOptions) BuildSnapshotArguments(string input, string output, IMediaAnalysis source, Size? size = null, TimeSpan? captureTime = null, int? streamIndex = null, int inputFileIndex = 0)
{
return BuildSnapshotArguments(input, VideoCodec.Image.GetByExtension(output), source, size, captureTime, streamIndex, inputFileIndex);
}
public static (FFMpegArguments, Action<FFMpegArgumentOptions> outputOptions) BuildSnapshotArguments(string input, IMediaAnalysis source, Size? size = null, TimeSpan? captureTime = null, int? streamIndex = null, int inputFileIndex = 0)
{
return BuildSnapshotArguments(input, VideoCodec.Image.Png, source, size, captureTime, streamIndex, inputFileIndex);
}
private static (FFMpegArguments, Action<FFMpegArgumentOptions> outputOptions) BuildSnapshotArguments(string input, Codec codec, IMediaAnalysis source, Size? size = null, TimeSpan? captureTime = null, int? streamIndex = null, int inputFileIndex = 0)
{
Codec codec2 = codec;
TimeSpan valueOrDefault = captureTime.GetValueOrDefault();
if (!captureTime.HasValue)
{
valueOrDefault = TimeSpan.FromSeconds(source.Duration.TotalSeconds / 3.0);
captureTime = valueOrDefault;
}
size = PrepareSnapshotSize(source, size);
int valueOrDefault2 = streamIndex.GetValueOrDefault();
if (!streamIndex.HasValue)
{
valueOrDefault2 = source.PrimaryVideoStream?.Index ?? source.VideoStreams.FirstOrDefault()?.Index ?? 0;
streamIndex = valueOrDefault2;
}
return (FFMpegArguments.FromFileInput(input, verifyExists: false, delegate(FFMpegArgumentOptions options)
{
options.Seek(captureTime);
}), delegate(FFMpegArgumentOptions options)
{
options.SelectStream(streamIndex.Value, inputFileIndex).WithVideoCodec(codec2).WithFrameOutputCount(1)
.Resize(size);
});
}
public static (FFMpegArguments, Action<FFMpegArgumentOptions> outputOptions) BuildGifSnapshotArguments(string input, IMediaAnalysis source, Size? size = null, TimeSpan? captureTime = null, TimeSpan? duration = null, int? streamIndex = null, double fps = 12.0)
{
Size defaultValue = new Size(480, -1);
TimeSpan valueOrDefault = captureTime.GetValueOrDefault();
if (!captureTime.HasValue)
{
valueOrDefault = TimeSpan.FromSeconds(source.Duration.TotalSeconds / 3.0);
captureTime = valueOrDefault;
}
size = PrepareSnapshotSize(source, size).GetValueOrDefault(defaultValue);
int valueOrDefault2 = streamIndex.GetValueOrDefault();
if (!streamIndex.HasValue)
{
valueOrDefault2 = source.PrimaryVideoStream?.Index ?? source.VideoStreams.FirstOrDefault()?.Index ?? 0;
streamIndex = valueOrDefault2;
}
return (FFMpegArguments.FromFileInput(input, verifyExists: false, delegate(FFMpegArgumentOptions options)
{
options.Seek(captureTime).WithDuration(duration);
}), delegate(FFMpegArgumentOptions options)
{
options.WithGifPaletteArgument(streamIndex.Value, size, fps);
});
}
private static Size? PrepareSnapshotSize(IMediaAnalysis source, Size? wantedSize)
{
if (!wantedSize.HasValue || (wantedSize.Value.Height <= 0 && wantedSize.Value.Width <= 0) || source.PrimaryVideoStream == null)
{
return null;
}
Size size = new Size(source.PrimaryVideoStream.Width, source.PrimaryVideoStream.Height);
if (IsRotated(source.PrimaryVideoStream.Rotation))
{
size = new Size(source.PrimaryVideoStream.Height, source.PrimaryVideoStream.Width);
}
if (wantedSize.Value.Width != size.Width || wantedSize.Value.Height != size.Height)
{
if (wantedSize.Value.Width <= 0 && wantedSize.Value.Height > 0)
{
double num = (double)wantedSize.Value.Height / (double)size.Height;
return new Size((int)((double)size.Width * num), (int)((double)size.Height * num));
}
if (wantedSize.Value.Height <= 0 && wantedSize.Value.Width > 0)
{
double num2 = (double)wantedSize.Value.Width / (double)size.Width;
return new Size((int)((double)size.Width * num2), (int)((double)size.Height * num2));
}
return wantedSize;
}
return null;
}
private static bool IsRotated(int rotation)
{
int num = Math.Abs(rotation);
if (num != 90)
{
return num == 180;
}
return true;
}
}
public class FFOptions : ICloneable
{
public string WorkingDirectory { get; set; } = string.Empty;
public string BinaryFolder { get; set; } = string.Empty;
public string TemporaryFilesFolder { get; set; } = Path.GetTempPath();
public string EncodingWebName { get; set; } = System.Text.Encoding.Default.WebName;
[JsonIgnore]
public Encoding Encoding
{
get
{
return System.Text.Encoding.GetEncoding(EncodingWebName);
}
set
{
EncodingWebName = value?.WebName ?? System.Text.Encoding.Default.WebName;
}
}
public FFMpegLogLevel? LogLevel { get; set; }
public Dictionary<string, string> ExtensionOverrides { get; set; } = new Dictionary<string, string> { { "mpegts", ".ts" } };
public bool UseCache { get; set; } = true;
object ICloneable.Clone()
{
return Clone();
}
public FFOptions Clone()
{
return (FFOptions)MemberwiseClone();
}
}
public class AudioStream : MediaStream
{
public int Channels { get; set; }
public string ChannelLayout { get; set; }
public int SampleRateHz { get; set; }
public string Profile { get; set; }
}
public static class FFProbe
{
public static IMediaAnalysis Analyse(string filePath, FFOptions? ffOptions = null, string? customArguments = null)
{
ThrowIfInputFileDoesNotExist(filePath);
IProcessResult obj = PrepareStreamAnalysisInstance(filePath, ffOptions ?? GlobalFFOptions.Current, customArguments).StartAndWaitForExit();
ThrowIfExitCodeNotZero(obj);
return ParseOutput(obj);
}
public static FFProbeFrames GetFrames(string filePath, FFOptions? ffOptions = null, string? customArguments = null)
{
ThrowIfInputFileDoesNotExist(filePath);
IProcessResult obj = PrepareFrameAnalysisInstance(filePath, ffOptions ?? GlobalFFOptions.Current, customArguments).StartAndWaitForExit();
ThrowIfExitCodeNotZero(obj);
return ParseFramesOutput(obj);
}
public static FFProbePackets GetPackets(string filePath, FFOptions? ffOptions = null, string? customArguments = null)
{
ThrowIfInputFileDoesNotExist(filePath);
IProcessResult obj = PreparePacketAnalysisInstance(filePath, ffOptions ?? GlobalFFOptions.Current, customArguments).StartAndWaitForExit();
ThrowIfExitCodeNotZero(obj);
return ParsePacketsOutput(obj);
}
public static IMediaAnalysis Analyse(Uri uri, FFOptions? ffOptions = null, string? customArguments = null)
{
IProcessResult obj = PrepareStreamAnalysisInstance(uri.AbsoluteUri, ffOptions ?? GlobalFFOptions.Current, customArguments).StartAndWaitForExit();
ThrowIfExitCodeNotZero(obj);
return ParseOutput(obj);
}
public static IMediaAnalysis Analyse(Stream stream, FFOptions? ffOptions = null, string? customArguments = null)
{
InputPipeArgument inputPipeArgument = new InputPipeArgument(new StreamPipeSource(stream));
ProcessArguments processArguments = PrepareStreamAnalysisInstance(inputPipeArgument.PipePath, ffOptions ?? GlobalFFOptions.Current, customArguments);
inputPipeArgument.Pre();
Task<IProcessResult> task = processArguments.StartAndWaitForExitAsync();
try
{
inputPipeArgument.During().ConfigureAwait(continueOnCapturedContext: false).GetAwaiter()
.GetResult();
}
catch (IOException)
{
}
finally
{
inputPipeArgument.Post();
}
IProcessResult result = task.ConfigureAwait(continueOnCapturedContext: false).GetAwaiter().GetResult();
ThrowIfExitCodeNotZero(result);
return ParseOutput(result);
}
public static async Task<IMediaAnalysis> AnalyseAsync(string filePath, FFOptions? ffOptions = null, CancellationToken cancellationToken = default(CancellationToken), string? customArguments = null)
{
ThrowIfInputFileDoesNotExist(filePath);
IProcessResult obj = await PrepareStreamAnalysisInstance(filePath, ffOptions ?? GlobalFFOptions.Current, customArguments).StartAndWaitForExitAsync(cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
cancellationToken.ThrowIfCancellationRequested();
ThrowIfExitCodeNotZero(obj);
return ParseOutput(obj);
}
public static FFProbeFrames GetFrames(Uri uri, FFOptions? ffOptions = null, string? customArguments = null)
{
IProcessResult obj = PrepareFrameAnalysisInstance(uri.AbsoluteUri, ffOptions ?? GlobalFFOptions.Current, customArguments).StartAndWaitForExit();
ThrowIfExitCodeNotZero(obj);
return ParseFramesOutput(obj);
}
public static async Task<FFProbeFrames> GetFramesAsync(string filePath, FFOptions? ffOptions = null, CancellationToken cancellationToken = default(CancellationToken), string? customArguments = null)
{
ThrowIfInputFileDoesNotExist(filePath);
return ParseFramesOutput(await PrepareFrameAnalysisInstance(filePath, ffOptions ?? GlobalFFOptions.Current, customArguments).StartAndWaitForExitAsync(cancellationToken).ConfigureAwait(continueOnCapturedContext: false));
}
public static async Task<FFProbePackets> GetPacketsAsync(string filePath, FFOptions? ffOptions = null, CancellationToken cancellationToken = default(CancellationToken), string? customArguments = null)
{
ThrowIfInputFileDoesNotExist(filePath);
return ParsePacketsOutput(await PreparePacketAnalysisInstance(filePath, ffOptions ?? GlobalFFOptions.Current, customArguments).StartAndWaitForExitAsync(cancellationToken).ConfigureAwait(continueOnCapturedContext: false));
}
public static async Task<IMediaAnalysis> AnalyseAsync(Uri uri, FFOptions? ffOptions = null, CancellationToken cancellationToken = default(CancellationToken), string? customArguments = null)
{
IProcessResult obj = await PrepareStreamAnalysisInstance(uri.AbsoluteUri, ffOptions ?? GlobalFFOptions.Current, customArguments).StartAndWaitForExitAsync(cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
cancellationToken.ThrowIfCancellationRequested();
ThrowIfExitCodeNotZero(obj);
return ParseOutput(obj);
}
public static async Task<IMediaAnalysis> AnalyseAsync(Stream stream, FFOptions? ffOptions = null, CancellationToken cancellationToken = default(CancellationToken), string? customArguments = null)
{
StreamPipeSource writer = new StreamPipeSource(stream);
InputPipeArgument pipeArgument = new InputPipeArgument(writer);
ProcessArguments processArguments = PrepareStreamAnalysisInstance(pipeArgument.PipePath, ffOptions ?? GlobalFFOptions.Current, customArguments);
pipeArgument.Pre();
Task<IProcessResult> task = processArguments.StartAndWaitForExitAsync(cancellationToken);
try
{
await pipeArgument.During(cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
}
catch (IOException)
{
}
finally
{
pipeArgument.Post();
}
IProcessResult obj = await task.ConfigureAwait(continueOnCapturedContext: false);
cancellationToken.ThrowIfCancellationRequested();
ThrowIfExitCodeNotZero(obj);
pipeArgument.Post();
return ParseOutput(obj);
}
public static async Task<FFProbeFrames> GetFramesAsync(Uri uri, FFOptions? ffOptions = null, CancellationToken cancellationToken = default(CancellationToken), string? customArguments = null)
{
return ParseFramesOutput(await PrepareFrameAnalysisInstance(uri.AbsoluteUri, ffOptions ?? GlobalFFOptions.Current, customArguments).StartAndWaitForExitAsync(cancellationToken).ConfigureAwait(continueOnCapturedContext: false));
}
private static IMediaAnalysis ParseOutput(IProcessResult instance)
{
FFProbeAnalysis? fFProbeAnalysis = JsonSerializer.Deserialize<FFProbeAnalysis>(string.Join(string.Empty, instance.OutputData), new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
});
if (fFProbeAnalysis?.Format == null)
{
throw new FormatNullException();
}
fFProbeAnalysis.ErrorData = instance.ErrorData;
return new MediaAnalysis(fFProbeAnalysis);
}
private static FFProbeFrames ParseFramesOutput(IProcessResult instance)
{
return JsonSerializer.Deserialize<FFProbeFrames>(string.Join(string.Empty, instance.OutputData), new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true,
NumberHandling = (JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)
});
}
private static FFProbePackets ParsePacketsOutput(IProcessResult instance)
{
return JsonSerializer.Deserialize<FFProbePackets>(string.Join(string.Empty, instance.OutputData), new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true,
NumberHandling = (JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)
});
}
private static void ThrowIfInputFileDoesNotExist(string filePath)
{
if (!File.Exists(filePath))
{
throw new FFMpegException(FFMpegExceptionType.File, "No file found at '" + filePath + "'");
}
}
private static void ThrowIfExitCodeNotZero(IProcessResult result)
{
if (result.ExitCode != 0)
{
string message = string.Format("ffprobe exited with non-zero exit-code ({0} - {1})", result.ExitCode, string.Join("\n", result.ErrorData));
throw new FFMpegException(FFMpegExceptionType.Process, message, null, string.Join("\n", result.ErrorData));
}
}
private static ProcessArguments PrepareStreamAnalysisInstance(string filePath, FFOptions ffOptions, string? customArguments)
{
return PrepareInstance("-loglevel error -print_format json -show_format -sexagesimal -show_streams -show_chapters \"" + filePath + "\"", ffOptions, customArguments);
}
private static ProcessArguments PrepareFrameAnalysisInstance(string filePath, FFOptions ffOptions, string? customArguments)
{
return PrepareInstance("-loglevel error -print_format json -show_frames -v quiet -sexagesimal \"" + filePath + "\"", ffOptions, customArguments);
}
private static ProcessArguments PreparePacketAnalysisInstance(string filePath, FFOptions ffOptions, string? customArguments)
{
return PrepareInstance("-loglevel error -print_format json -show_packets -v quiet -sexagesimal \"" + filePath + "\"", ffOptions, customArguments);
}
private static ProcessArguments PrepareInstance(string arguments, FFOptions ffOptions, string? customArguments)
{
//IL_0046: Unknown result type (might be due to invalid IL or missing references)
//IL_004c: Expected O, but got Unknown
FFProbeHelper.RootExceptionCheck();
FFProbeHelper.VerifyFFProbeExists(ffOptions);
return new ProcessArguments(new ProcessStartInfo(GlobalFFOptions.GetFFProbeBinaryPath(ffOptions), arguments + " " + customArguments)
{
StandardOutputEncoding = ffOptions.Encoding,
StandardErrorEncoding = ffOptions.Encoding,
WorkingDirectory = ffOptions.WorkingDirectory
});
}
}
public class FFProbeAnalysis
{
[JsonPropertyName("streams")]
public List<FFProbeStream> Streams { get; set; }
[JsonPropertyName("format")]
public Format Format { get; set; }
[JsonPropertyName("chapters")]
public List<Chapter> Chapters { get; set; }
[JsonIgnore]
public IReadOnlyList<string> ErrorData { get; set; } = new List<string>();
}
public class FFProbeStream : ITagsContainer, IDispositionContainer
{
[JsonPropertyName("index")]
public int Index { get; set; }
[JsonPropertyName("avg_frame_rate")]
public string AvgFrameRate { get; set; }
[JsonPropertyName("bits_per_raw_sample")]
public string BitsPerRawSample { get; set; }
[JsonPropertyName("bits_per_sample")]
public int BitsPerSample { get; set; }
[JsonPropertyName("bit_rate")]
public string BitRate { get; set; }
[JsonPropertyName("channels")]
public int? Channels { get; set; }
[JsonPropertyName("channel_layout")]
public string ChannelLayout { get; set; }
[JsonPropertyName("codec_type")]
public string CodecType { get; set; }
[JsonPropertyName("codec_name")]
public string CodecName { get; set; }
[JsonPropertyName("codec_long_name")]
public string CodecLongName { get; set; }
[JsonPropertyName("codec_tag")]
public string CodecTag { get; set; }
[JsonPropertyName("codec_tag_string")]
public string CodecTagString { get; set; }
[JsonPropertyName("display_aspect_ratio")]
public string DisplayAspectRatio { get; set; }
[JsonPropertyName("sample_aspect_ratio")]
public string SampleAspectRatio { get; set; }
[JsonPropertyName("start_time")]
public string StartTime { get; set; }
[JsonPropertyName("duration")]
public string Duration { get; set; }
[JsonPropertyName("profile")]
public string Profile { get; set; }
[JsonPropertyName("width")]
public int? Width { get; set; }
[JsonPropertyName("height")]
public int? Height { get; set; }
[JsonPropertyName("r_frame_rate")]
public string FrameRate { get; set; }
[JsonPropertyName("pix_fmt")]
public string PixelFormat { get; set; }
[JsonPropertyName("level")]
public int Level { get; set; }
[JsonPropertyName("sample_rate")]
public string SampleRate { get; set; }
[JsonPropertyName("side_data_list")]
public List<Dictionary<string, JsonValue>> SideData { get; set; }
[JsonPropertyName("color_range")]
public string ColorRange { get; set; }
[JsonPropertyName("color_space")]
public string ColorSpace { get; set; }
[JsonPropertyName("color_transfer")]
public string ColorTransfer { get; set; }
[JsonPropertyName("color_primaries")]
public string ColorPrimaries { get; set; }
[JsonPropertyName("disposition")]
public Dictionary<string, int> Disposition { get; set; }
[JsonPropertyName("tags")]
public Dictionary<string, string>? Tags { get; set; }
}
public class Format : ITagsContainer
{
[JsonPropertyName("filename")]
public string Filename { get; set; }
[JsonPropertyName("nb_streams")]
public int NbStreams { get; set; }
[JsonPropertyName("nb_programs")]
public int NbPrograms { get; set; }
[JsonPropertyName("format_name")]
public string FormatName { get; set; }
[JsonPropertyName("format_long_name")]
public string FormatLongName { get; set; }
[JsonPropertyName("start_time")]
public string StartTime { get; set; }
[JsonPropertyName("duration")]
public string Duration { get; set; }
[JsonPropertyName("size")]
public string Size { get; set; }
[JsonPropertyName("bit_rate")]
public string? BitRate { get; set; }
[JsonPropertyName("probe_score")]
public int ProbeScore { get; set; }
[JsonPropertyName("tags")]
public Dictionary<string, string>? Tags { get; set; }
}
public class Chapter : ITagsContainer
{
[JsonPropertyName("id")]
public long Id { get; set; }
[JsonPropertyName("time_base")]
public string TimeBase { get; set; }
[JsonPropertyName("start")]
public long Start { get; set; }
[JsonPropertyName("start_time")]
public string StartTime { get; set; }
[JsonPropertyName("end")]
public long End { get; set; }
[JsonPropertyName("end_time")]
public string EndTime { get; set; }
[JsonPropertyName("tags")]
public Dictionary<string, string>? Tags { get; set; }
}
public interface IDispositionContainer
{
Dictionary<string, int> Disposition { get; set; }
}
public interface ITagsContainer
{
Dictionary<string, string>? Tags { get; set; }
}
public static class TagExtensions
{
private static string? TryGetTagValue(ITagsContainer tagsContainer, string key)
{
if (tagsContainer.Tags != null && tagsContainer.Tags.TryGetValue(key, out string value))
{
return value;
}
return null;
}
public static string? GetLanguage(this ITagsContainer tagsContainer)
{
return TryGetTagValue(tagsContainer, "language");
}
public static string? GetCreationTime(this ITagsContainer tagsContainer)
{
return TryGetTagValue(tagsContainer, "creation_time");
}
public static string? GetRotate(this ITagsContainer tagsContainer)
{
return TryGetTagValue(tagsContainer, "rotate");
}
public static string? GetDuration(this ITagsContainer tagsContainer)
{
return TryGetTagValue(tagsContainer, "duration");
}
}
public static class DispositionExtensions
{
private static int? TryGetDispositionValue(IDispositionContainer dispositionContainer, string key)
{
if (dispositionContainer.Disposition != null && dispositionContainer.Disposition.TryGetValue(key, out var value))
{
return value;
}
return null;
}
public static int? GetDefault(this IDispositionContainer tagsContainer)
{
return TryGetDispositionValue(tagsContainer, "default");
}
public static int? GetForced(this IDispositionContainer tagsContainer)
{
return TryGetDispositionValue(tagsContainer, "forced");
}
}
public class FFProbeFrameAnalysis
{
[JsonPropertyName("media_type")]
public string MediaType { get; set; }
[JsonPropertyName("stream_index")]
public int StreamIndex { get; set; }
[JsonPropertyName("key_frame")]
public int KeyFrame { get; set; }
[JsonPropertyName("pkt_pts")]
public long PacketPts { get; set; }
[JsonPropertyName("pkt_pts_time")]
public string PacketPtsTime { get; set; }
[JsonPropertyName("pkt_dts")]
public long PacketDts { get; set; }
[JsonPropertyName("pkt_dts_time")]
public string PacketDtsTime { get; set; }
[JsonPropertyName("best_effort_timestamp")]
public long BestEffortTimestamp { get; set; }
[JsonPropertyName("best_effort_timestamp_time")]
public string BestEffortTimestampTime { get; set; }
[JsonPropertyName("pkt_duration")]
public int PacketDuration { get; set; }
[JsonPropertyName("pkt_duration_time")]
public string PacketDurationTime { get; set; }
[JsonPropertyName("pkt_pos")]
public long PacketPos { get; set; }
[JsonPropertyName("pkt_size")]
public int PacketSize { get; set; }
[JsonPropertyName("width")]
public long Width { get; set; }
[JsonPropertyName("height")]
public long Height { get; set; }
[JsonPropertyName("pix_fmt")]
public string PixelFormat { get; set; }
[JsonPropertyName("pict_type")]
public string PictureType { get; set; }
[JsonPropertyName("coded_picture_number")]
public long CodedPictureNumber { get; set; }
[JsonPropertyName("display_picture_number")]
public long DisplayPictureNumber { get; set; }
[JsonPropertyName("interlaced_frame")]
public int InterlacedFrame { get; set; }
[JsonPropertyName("top_field_first")]
public int TopFieldFirst { get; set; }
[JsonPropertyName("repeat_pict")]
public int RepeatPicture { get; set; }
[JsonPropertyName("chroma_location")]
public string ChromaLocation { get; set; }
}
public class FFProbeFrames
{
[JsonPropertyName("frames")]
public List<FFProbeFrameAnalysis> Frames { get; set; }
}
public interface IMediaAnalysis
{
TimeSpan Duration { get; }
MediaFormat Format { get; }
List<ChapterData> Chapters { get; }
AudioStream? PrimaryAudioStream { get; }
VideoStream? PrimaryVideoStream { get; }
SubtitleStream? PrimarySubtitleStream { get; }
List<VideoStream> VideoStreams { get; }
List<AudioStream> AudioStreams { get; }
List<SubtitleStream> SubtitleStreams { get; }
IReadOnlyList<string> ErrorData { get; }
}
internal class MediaAnalysis : IMediaAnalysis
{
public TimeSpan Duration => new TimeSpan[3]
{
Format.Duration,
PrimaryVideoStream?.Duration ?? TimeSpan.Zero,
PrimaryAudioStream?.Duration ?? TimeSpan.Zero
}.Max();
public MediaFormat Format { get; }
public List<ChapterData> Chapters { get; }
public AudioStream? PrimaryAudioStream => AudioStreams.OrderBy((AudioStream stream) => stream.Index).FirstOrDefault();
public VideoStream? PrimaryVideoStream => VideoStreams.OrderBy((VideoStream stream) => stream.Index).FirstOrDefault();
public SubtitleStream? PrimarySubtitleStream => SubtitleStreams.OrderBy((SubtitleStream stream) => stream.Index).FirstOrDefault();
public List<VideoStream> VideoStreams { get; }
public List<AudioStream> AudioStreams { get; }
public List<SubtitleStream> SubtitleStreams { get; }
public IReadOnlyList<string> ErrorData { get; }
internal MediaAnalysis(FFProbeAnalysis analysis)
{
Format = ParseFormat(analysis.Format);
Chapters = analysis.Chapters.Select((Chapter c) => ParseChapter(c)).ToList();
VideoStreams = analysis.Streams.Where((FFProbeStream stream) => stream.CodecType == "video").Select(ParseVideoStream).ToList();
AudioStreams = analysis.Streams.Where((FFProbeStream stream) => stream.CodecType == "audio").Select(ParseAudioStream).ToList();
SubtitleStreams = analysis.Streams.Where((FFProbeStream stream) => stream.CodecType == "subtitle").Select(ParseSubtitleStream).ToList();
ErrorData = analysis.ErrorData;
}
private MediaFormat ParseFormat(Format analysisFormat)
{
return new MediaFormat
{
Duration = MediaAnalysisUtils.ParseDuration(analysisFormat.Duration),
StartTime = MediaAnalysisUtils.ParseDuration(analysisFormat.StartTime),
FormatName = analysisFormat.FormatName,
FormatLongName = analysisFormat.FormatLongName,
StreamCount = analysisFormat.NbStreams,
ProbeScore = analysisFormat.ProbeScore,
BitRate = long.Parse(analysisFormat.BitRate ?? "0"),
Tags = analysisFormat.Tags.ToCaseInsensitive()
};
}
private string GetValue(string tagName, Dictionary<string, string>? tags, string defaultValue)
{
if (tags != null)
{
if (!tags.TryGetValue(tagName, out string value))
{
return defaultValue;
}
return value;
}
return defaultValue;
}
private ChapterData ParseChapter(Chapter analysisChapter)
{
string value = GetValue("title", analysisChapter.Tags, "TitleValueNotSet");
TimeSpan start = MediaAnalysisUtils.ParseDuration(analysisChapter.StartTime);
TimeSpan end = MediaAnalysisUtils.ParseDuration(analysisChapter.EndTime);
return new ChapterData(value, start, end);
}
private int? GetBitDepth(FFProbeStream stream)
{
int result;
int num = (int.TryParse(stream.BitsPerRawSample, out result) ? result : stream.BitsPerSample);
if (num != 0)
{
return num;
}
return null;
}
private VideoStream ParseVideoStream(FFProbeStream stream)
{
return new VideoStream
{
Index = stream.Index,
AvgFrameRate = MediaAnalysisUtils.DivideRatio(MediaAnalysisUtils.ParseRatioDouble(stream.AvgFrameRate, '/')),
BitRate = ((!string.IsNullOrEmpty(stream.BitRate)) ? MediaAnalysisUtils.ParseLongInvariant(stream.BitRate) : 0),
BitsPerRawSample = ((!string.IsNullOrEmpty(stream.BitsPerRawSample)) ? MediaAnalysisUtils.ParseIntInvariant(stream.BitsPerRawSample) : 0),
CodecName = stream.CodecName,
CodecLongName = stream.CodecLongName,
CodecTag = stream.CodecTag,
CodecTagString = stream.CodecTagString,
DisplayAspectRatio = MediaAnalysisUtils.ParseRatioInt(stream.DisplayAspectRatio, ':'),
SampleAspectRatio = MediaAnalysisUtils.ParseRatioInt(stream.SampleAspectRatio, ':'),
Duration = MediaAnalysisUtils.ParseDuration(stream.Duration),
StartTime = MediaAnalysisUtils.ParseDuration(stream.StartTime),
FrameRate = MediaAnalysisUtils.DivideRatio(MediaAnalysisUtils.ParseRatioDouble(stream.FrameRate, '/')),
Height = stream.Height.GetValueOrDefault(),
Width = stream.Width.GetValueOrDefault(),
Profile = stream.Profile,
PixelFormat = stream.PixelFormat,
Level = stream.Level,
ColorRange = stream.ColorRange,
ColorSpace = stream.ColorSpace,
ColorTransfer = stream.ColorTransfer,
ColorPrimaries = stream.ColorPrimaries,
Rotation = MediaAnalysisUtils.ParseRotation(stream),
Language = stream.GetLanguage(),
Disposition = MediaAnalysisUtils.FormatDisposition(stream.Disposition),
Tags = stream.Tags.ToCaseInsensitive(),
BitDepth = GetBitDepth(stream)
};
}
private AudioStream ParseAudioStream(FFProbeStream stream)
{
return new AudioStream
{
Index = stream.Index,
BitRate = ((!string.IsNullOrEmpty(stream.BitRate)) ? MediaAnalysisUtils.ParseLongInvariant(stream.BitRate) : 0),
CodecName = stream.CodecName,
CodecLongName = stream.CodecLongName,
CodecTag = stream.CodecTag,
CodecTagString = stream.CodecTagString,
Channels = stream.Channels.GetValueOrDefault(),
ChannelLayout = stream.ChannelLayout,
Duration = MediaAnalysisUtils.ParseDuration(stream.Duration),
StartTime = MediaAnalysisUtils.ParseDuration(stream.StartTime),
SampleRateHz = ((!string.IsNullOrEmpty(stream.SampleRate)) ? MediaAnalysisUtils.ParseIntInvariant(stream.SampleRate) : 0),
Profile = stream.Profile,
Language = stream.GetLanguage(),
Disposition = MediaAnalysisUtils.FormatDisposition(stream.Disposition),
Tags = stream.Tags.ToCaseInsensitive(),
BitDepth = GetBitDepth(stream)
};
}
private SubtitleStream ParseSubtitleStream(FFProbeStream stream)
{
return new SubtitleStream
{
Index = stream.Index,
BitRate = ((!string.IsNullOrEmpty(stream.BitRate)) ? MediaAnalysisUtils.ParseLongInvariant(stream.BitRate) : 0),
CodecName = stream.CodecName,
CodecLongName = stream.CodecLongName,
Duration = MediaAnalysisUtils.ParseDuration(stream.Duration),
StartTime = MediaAnalysisUtils.ParseDuration(stream.StartTime),
Language = stream.GetLanguage(),
Disposition = MediaAnalysisUtils.FormatDisposition(stream.Disposition),
Tags = stream.Tags.ToCaseInsensitive()
};
}
}
public static class MediaAnalysisUtils
{
private static readonly Regex DurationRegex = new Regex("^(\\d+):(\\d{1,2}):(\\d{1,2})\\.(\\d{1,3})", RegexOptions.Compiled);
internal static Dictionary<string, string> ToCaseInsensitive(this Dictionary<string, string>? dictionary)
{
return dictionary?.ToDictionary<KeyValuePair<string, string>, string, string>((KeyValuePair<string, string> tag) => tag.Key, (KeyValuePair<string, string> tag) => tag.Value, StringComparer.OrdinalIgnoreCase) ?? new Dictionary<string, string>();
}
public static double DivideRatio((double, double) ratio)
{
return ratio.Item1 / ratio.Item2;
}
public static (int, int) ParseRatioInt(string input, char separator)
{
if (string.IsNullOrEmpty(input))
{
return (0, 0);
}
string[] array = input.Split(new char[1] { separator });
return (ParseIntInvariant(array[0]), ParseIntInvariant(array[1]));
}
public static (double, double) ParseRatioDouble(string input, char separator)
{
if (string.IsNullOrEmpty(input))
{
return (0.0, 0.0);
}
string[] array = input.Split(new char[1] { separator });
return ((array.Length != 0) ? ParseDoubleInvariant(array[0]) : 0.0, (array.Length > 1) ? ParseDoubleInvariant(array[1]) : 0.0);
}
public static double ParseDoubleInvariant(string line)
{
return double.Parse(line, NumberStyles.Any, CultureInfo.InvariantCulture);
}
public static int ParseIntInvariant(string line)
{
return int.Parse(line, NumberStyles.Any, CultureInfo.InvariantCulture);
}
public static long ParseLongInvariant(string line)
{
return long.Parse(line, NumberStyles.Any, CultureInfo.InvariantCulture);
}
public static TimeSpan ParseDuration(string duration)
{
if (!string.IsNullOrEmpty(duration))
{
Match match = DurationRegex.Match(duration);
if (match.Success)
{
string text = match.Groups[4].Value;
if (text.Length < 3)
{
text = text.PadRight(3, '0');
}
int hours = int.Parse(match.Groups[1].Value);
int minutes = int.Parse(match.Groups[2].Value);
int seconds = int.Parse(match.Groups[3].Value);
int milliseconds = int.Parse(text);
return new TimeSpan(0, hours, minutes, seconds, milliseconds);
}
return TimeSpan.Zero;
}
return TimeSpan.Zero;
}
public static int ParseRotation(FFProbeStream fFProbeStream)
{
JsonValue value2;
Dictionary<string, JsonValue>? obj = fFProbeStream.SideData?.Find((Dictionary<string, JsonValue> item) => item.TryGetValue("side_data_type", out value2) && value2.ToString() == "Display Matrix");
if (obj != null && obj.TryGetValue("rotation", out JsonValue value))
{
return (int)float.Parse(value.ToString());
}
return (int)float.Parse(fFProbeStream.GetRotate() ?? "0");
}
public static Dictionary<string, bool>? FormatDisposition(Dictionary<string, int>? disposition)
{
if (disposition == null)
{
return null;
}
Dictionary<string, bool> dictionary = new Dictionary<string, bool>(disposition.Count, StringComparer.Ordinal);
foreach (KeyValuePair<string, int> item in disposition)
{
dictionary.Add(item.Key, ToBool(item.Value));
}
return dictionary;
static bool ToBool(int value)
{
return value switch
{
0 => false,
1 => true,
_ => throw new ArgumentOutOfRangeException("value", $"Not expected disposition state value: {value}"),
};
}
}
}
public class MediaFormat : ITagsContainer
{
public TimeSpan Duration { get; set; }
public TimeSpan StartTime { get; set; }
public string FormatName { get; set; }
public string FormatLongName { get; set; }
public int StreamCount { get; set; }
public double ProbeScore { get; set; }
public double BitRate { get; set; }
public Dictionary<string, string>? Tags { get; set; }
}
public abstract class MediaStream : ITagsContainer
{
public int Index { get; set; }
public string CodecName { get; set; }
public string CodecLongName { get; set; }
public string CodecTagString { get; set; }
public string CodecTag { get; set; }
public long BitRate { get; set; }
public TimeSpan StartTime { get; set; }
public TimeSpan Duration { get; set; }
public string? Language { get; set; }
public Dictionary<string, bool>? Disposition { get; set; }
public int? BitDepth { get; set; }
public Dictionary<string, string>? Tags { get; set; }
public Codec GetCodecInfo()
{
return FFMpeg.GetCodec(CodecName);
}
}
public class FFProbePacketAnalysis
{
[JsonPropertyName("codec_type")]
public string CodecType { get; set; }
[JsonPropertyName("stream_index")]
public int StreamIndex { get; set; }
[JsonPropertyName("pts")]
public long Pts { get; set; }
[JsonPropertyName("pts_time")]
public string PtsTime { get; set; }
[JsonPropertyName("dts")]
public long Dts { get; set; }
[JsonPropertyName("dts_time")]
public string DtsTime { get; set; }
[JsonPropertyName("duration")]
public int Duration { get; set; }
[JsonPropertyName("duration_time")]
public string DurationTime { get; set; }
[JsonPropertyName("size")]
public int Size { get; set; }
[JsonPropertyName("pos")]
public long Pos { get; set; }
[JsonPropertyName("flags")]
public string Flags { get; set; }
}
public class FFProbePackets
{
[JsonPropertyName("packets")]
public List<FFProbePacketAnalysis> Packets { get; set; }
}
public static class ProcessArgumentsExtensions
{
public static IProcessResult StartAndWaitForExit(this ProcessArguments processArguments)
{
IProcessInstance val = processArguments.Start();
try
{
return val.WaitForExit();
}
finally
{
((IDisposable)val)?.Dispose();
}
}
public static async Task<IProcessResult> StartAndWaitForExitAsync(this ProcessArguments processArguments, CancellationToken cancellationToken = default(CancellationToken))
{
IProcessInstance instance = processArguments.Start();
try
{
return await instance.WaitForExitAsync(cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
}
finally
{
((IDisposable)instance)?.Dispose();
}
}
}
public class SubtitleStream : MediaStream
{
}
public class VideoStream : MediaStream
{
public double AvgFrameRate { get; set; }
public int BitsPerRawSample { get; set; }
public (int Width, int Height) DisplayAspectRatio { get; set; }
public (int Width, int Height) SampleAspectRatio { get; set; }
public string Profile { get; set; }
public int Width { get; set; }
public int Height { get; set; }
public double FrameRate { get; set; }
public string PixelFormat { get; set; }
public int Level { get; set; }
public int Rotation { get; set; }
public double AverageFrameRate { get; set; }
public string ColorRange { get; set; }
public string ColorSpace { get; set; }
public string ColorTransfer { get; set; }
public string ColorPrimaries { get; set; }
public PixelFormat GetPixelFormatInfo()
{
return FFMpeg.GetPixelFormat(PixelFormat);
}
}
public static class GlobalFFOptions
{
private const string ConfigFile = "ffmpeg.config.json";
private static FFOptions? _current;
public static FFOptions Current => _current ?? (_current = LoadFFOptions());
public static void Configure(Action<FFOptions> optionsAction)
{
optionsAction(Current);
}
public static void Configure(FFOptions ffOptions)
{
_current = ffOptions ?? throw new ArgumentNullException("ffOptions");
}
public static string GetFFMpegBinaryPath(FFOptions? ffOptions = null)
{
return GetFFBinaryPath("FFMpeg", ffOptions ?? Current);
}
public static string GetFFProbeBinaryPath(FFOptions? ffOptions = null)
{
return GetFFBinaryPath("FFProbe", ffOptions ?? Current);
}
private static string GetFFBinaryPath(string name, FFOptions ffOptions)
{
string text = name.ToLowerInvariant();
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
text += ".exe";
}
string path = (Environment.Is64BitProcess ? "x64" : "x86");
foreach (string item in new List<string>
{
Path.Combine(ffOptions.BinaryFolder, path),
ffOptions.BinaryFolder
})
{
string text2 = Path.Combine(item, text);
if (File.Exists(text2))
{
return text2;
}
}
return text;
}
private static FFOptions LoadFFOptions()
{
if (!File.Exists("ffmpeg.config.json"))
{
return new FFOptions();
}
return JsonSerializer.Deserialize<FFOptions>(File.ReadAllText("ffmpeg.config.json"));
}
}
}
namespace FFMpegCore.Helpers
{
public static class FFMpegHelper
{
private static bool _ffmpegVerified;
public static void ConversionSizeExceptionCheck(IMediaAnalysis info)
{
ConversionSizeExceptionCheck(info.PrimaryVideoStream.Width, info.PrimaryVideoStream.Height);
}
public static void ConversionSizeExceptionCheck(int width, int height)
{
if (height % 2 != 0 || width % 2 != 0)
{
throw new ArgumentException("FFMpeg yuv420p encoding requires the width and height to be a multiple of 2!");
}
}
public static void ExtensionExceptionCheck(string filename, string extension)
{
if (!extension.Equals(Path.GetExtension(filename), StringComparison.OrdinalIgnoreCase))
{
throw new FFMpegException(FFMpegExceptionType.File, "Invalid output file. File extension should be '" + extension + "' required.");
}
}
public static void RootExceptionCheck()
{
if (GlobalFFOptions.Current.BinaryFolder == null)
{
throw new FFOptionsException("FFMpeg root is not configured in app config. Missing key 'BinaryFolder'.");
}
}
public static void VerifyFFMpegExists(FFOptions ffMpegOptions)
{
if (!_ffmpegVerified)
{
_ffmpegVerified = Instance.Finish(GlobalFFOptions.GetFFMpegBinaryPath(ffMpegOptions), "-version", (EventHandler<string>)null, (EventHandler<string>)null).ExitCode == 0;
if (!_ffmpegVerified)
{
throw new FFMpegException(FFMpegExceptionType.Operation, "ffmpeg was not found on your system");
}
}
}
}
public static class FFProbeHelper
{
private static bool _ffprobeVerified;
public static void RootExceptionCheck()
{
if (GlobalFFOptions.Current.BinaryFolder == null)
{
throw new FFOptionsException("FFProbe root is not configured in app config. Missing key 'BinaryFolder'.");
}
}
public static void VerifyFFProbeExists(FFOptions ffMpegOptions)
{
if (!_ffprobeVerified)
{
_ffprobeVerified = Instance.Finish(GlobalFFOptions.GetFFProbeBinaryPath(ffMpegOptions), "-version", (EventHandler<string>)null, (EventHandler<string>)null).ExitCode == 0;
if (!_ffprobeVerified)
{
throw new FFProbeException("ffprobe was not found on your system");
}
}
}
}
}
namespace FFMpegCore.Pipes
{
public interface IAudioSample
{
void Serialize(Stream stream);
Task SerializeAsync(Stream stream, CancellationToken token);
}
public interface IPipeSink
{
Task ReadAsync(Stream inputStream, CancellationToken cancellationToken);
string GetFormat();
}
public interface IPipeSource
{
string GetStreamArguments();
Task WriteAsync(Stream outputStream, CancellationToken cancellationToken);
}
public interface IVideoFrame
{
int Width { get; }
int Height { get; }
string Format { get; }
void Serialize(Stream pipe);
Task SerializeAsync(Stream pipe, CancellationToken token);
}
internal static class PipeHelpers
{
public static string GetUniquePipeName()
{
return "FFMpegCore_" + Guid.NewGuid().ToString("N").Substring(0, 16);
}
public static string GetPipePath(string pipeName)
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
return "\\\\.\\pipe\\" + pipeName;
}
return "unix:" + Path.Combine(Path.GetTempPath(), "CoreFxPipe_" + pipeName);
}
}
public class RawAudioPipeSource : IPipeSource
{
private readonly IEnumerator<IAudioSample> _sampleEnumerator;
public string Format { get; set; } = "s16le";
public uint SampleRate { get; set; } = 8000u;
public uint Channels { get; set; } = 1u;
public RawAudioPipeSource(IEnumerator<IAudioSample> sampleEnumerator)
{
_sampleEnumerator = sampleEnumerator;
}
public RawAudioPipeSource(IEnumerable<IAudioSample> sampleEnumerator)
: this(sampleEnumerator.GetEnumerator())
{
}
public string GetStreamArguments()
{
return $"-f {Format} -ar {SampleRate} -ac {Channels}";
}
public async Task WriteAsync(Stream outputStream, CancellationToken cancellationToken)
{
if (_sampleEnumerator.MoveNext() && _sampleEnumerator.Current != null)
{
await _sampleEnumerator.Current.SerializeAsync(outputStream, cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
}
while (_sampleEnumerator.MoveNext())
{
await _sampleEnumerator.Current.SerializeAsync(outputStream, cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
}
}
}
public class RawVideoPipeSource : IPipeSource
{
private readonly IEnumerator<IVideoFrame> _framesEnumerator;
private bool _formatInitialized;
public string StreamFormat { get; private set; }
public int Width { get; private set; }
public int Height { get; private set; }
public double FrameRate { get; set; } = 25.0;
public RawVideoPipeSource(IEnumerable<IVideoFrame> framesEnumerator)
{
_framesEnumerator = framesEnumerator.GetEnumerator();
}
public string GetStreamArguments()
{
if (!_formatInitialized)
{
if (_framesEnumerator.Current == null && !_framesEnumerator.MoveNext())
{
throw new InvalidOperationException("Enumerator is empty, unable to get frame");
}
StreamFormat = _framesEnumerator.Current.Format;
Width = _framesEnumerator.Current.Width;
Height = _framesEnumerator.Current.Height;
_formatInitialized = true;
}
return $"-f rawvideo -r {FrameRate.ToString(CultureInfo.InvariantCulture)} -pix_fmt {StreamFormat} -s {Width}x{Height}";
}
public async Task WriteAsync(Stream outputStream, CancellationToken cancellationToken)
{
if (_framesEnumerator.Current != null)
{
CheckFrameAndThrow(_framesEnumerator.Current);
await _framesEnumerator.Current.SerializeAsync(outputStream, cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
}
while (_framesEnumerator.MoveNext())
{
CheckFrameAndThrow(_framesEnumerator.Current);
await _framesEnumerator.Current.SerializeAsync(outputStream, cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
}
}
private void CheckFrameAndThrow(IVideoFrame frame)
{
if (frame.Width != Width || frame.Height != Height || frame.Format != StreamFormat)
{
throw new FFMpegStreamFormatException(FFMpegExceptionType.Operation, "Video frame is not the same format as created raw video stream\r\n" + $"Frame format: {frame.Width}x{frame.Height} pix_fmt: {frame.Format}\r\n" + $"Stream format: {Width}x{Height} pix_fmt: {StreamFormat}");
}
}
}
public class StreamPipeSink : IPipeSink
{
public Func<Stream, CancellationToken, Task> Writer { get; }
public int BlockSize { get; set; } = 4096;
public string Format { get; set; } = string.Empty;
public StreamPipeSink(Func<Stream, CancellationToken, Task> writer)
{
Writer = writer;
}
public StreamPipeSink(Stream destination)
{
Stream destination2 = destination;
base..ctor();
StreamPipeSink streamPipeSink = this;
Writer = (Stream inputStream, CancellationToken cancellationToken) => inputStream.CopyToAsync(destination2, streamPipeSink.BlockSize, cancellationToken);
}
public async Task ReadAsync(Stream inputStream, CancellationToken cancellationToken)
{
await Writer(inputStream, cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
}
public string GetFormat()
{
return Format;
}
}
public class StreamPipeSource : IPipeSource
{
public Stream Source { get; }
public int BlockSize { get; } = 4096;
public string StreamFormat { get; } = string.Empty;
public StreamPipeSource(Stream source)
{
Source = source;
}
public string GetStreamArguments()
{
return StreamFormat;
}
public Task WriteAsync(Stream outputStream, CancellationToken cancellationToken)
{
return Source.CopyToAsync(outputStream, BlockSize, cancellationToken);
}
}
}
namespace FFMpegCore.Exceptions
{
public enum FFMpegExceptionType
{
Conversion,
File,
Operation,
Process
}
public class FFMpegException : Exception
{
public FFMpegExceptionType Type { get; }
public string FFMpegErrorOutput { get; }
public FFMpegException(FFMpegExceptionType type, string message, Exception? innerException = null, string ffMpegErrorOutput = "")
: base(message, innerException)
{
FFMpegErrorOutput = ffMpegErrorOutput;
Type = type;
}
public FFMpegException(FFMpegExceptionType type, string message, string ffMpegErrorOutput = "")
: base(message)
{
FFMpegErrorOutput = ffMpegErrorOutput;
Type = type;
}
public FFMpegException(FFMpegExceptionType type, string message)
: base(message)
{
FFMpegErrorOutput = string.Empty;
Type = type;
}
}
public class FFOptionsException : Exception
{
public FFOptionsException(string message, Exception? innerException = null)
: base(message, innerException)
{
}
}
public class FFMpegArgumentException : Exception
{
public FFMpegArgumentException(string? message = null, Exception? innerException = null)
: base(message, innerException)
{
}
}
public class FFMpegStreamFormatException : FFMpegException
{
public FFMpegStreamFormatException(FFMpegExceptionType type, string message, Exception? innerException = null)
: base(type, message, innerException)
{
}
}
public class FFProbeException : Exception
{
public FFProbeException(string message, Exception? inner = null)
: base(message, inner)
{
}
}
public class FFProbeProcessException : FFProbeException
{
public IReadOnlyCollection<string> ProcessErrors { get; }
public FFProbeProcessException(string message, IReadOnlyCollection<string> processErrors, Exception? inner = null)
: base(message, inner)
{
ProcessErrors = processErrors;
}
}
public class FormatNullException : FFProbeException
{
public FormatNullException()
: base("Format not specified")
{
}
}
}
namespace FFMpegCore.Enums
{
public enum AudioQuality
{
Ultra = 384,
VeryHigh = 256,
Good = 192,
Normal = 128,
BelowNormal = 96,
Low = 64
}
public enum FeatureStatus
{
Unknown,
NotSupported,
Supported
}
public class Codec
{
public class FeatureLevel
{
public bool IsExperimental { get; internal set; }
public FeatureStatus SupportsFrameLevelMultithreading { get; internal set; }
public FeatureStatus SupportsSliceLevelMultithreading { get; internal set; }
public FeatureStatus SupportsDrawHorizBand { get; internal set; }
public FeatureStatus SupportsDirectRendering { get; internal set; }
internal void Merge(FeatureLevel other)
{
IsExperimental |= other.IsExperimental;
SupportsFrameLevelMultithreading = (FeatureStatus)Math.Max((int)SupportsFrameLevelMultithreading, (int)other.SupportsFrameLevelMultithreading);
SupportsSliceLevelMultithreading = (FeatureStatus)Math.Max((int)SupportsSliceLevelMultithreading, (int)other.SupportsSliceLevelMultithreading);
SupportsDrawHorizBand = (FeatureStatus)Math.Max((int)SupportsDrawHorizBand, (int)other.SupportsDrawHorizBand);
SupportsDirectRendering = (FeatureStatus)Math.Max((int)SupportsDirectRendering, (int)other.SupportsDirectRendering);
}
}
private static readonly Regex _codecsFormatRegex = new Regex("([D\\.])([E\\.])([VASD\\.])([I\\.])([L\\.])([S\\.])\\s+([a-z0-9_-]+)\\s+(.+)");
private static readonly Regex _decodersEncodersFormatRegex = new Regex("([VASD\\.])([F\\.])([S\\.])([X\\.])([B\\.])([D\\.])\\s+([a-z0-9_-]+)\\s+(.+)");
public string Name { get; }
public CodecType Type { get; private set; }
public bool DecodingSupported { get; private set; }
public bool EncodingSupported { get; private set; }
public bool IsIntraFrameOnly { get; private set; }
public bool IsLossy { get; private set; }
public bool IsLossless { get; private set; }
public string Description { get; private set; }
public FeatureLevel EncoderFeatureLevel { get; }
public FeatureLevel DecoderFeatureLevel { get; }
internal Codec(string name, CodecType type)
{
EncoderFeatureLevel = new FeatureLevel();
DecoderFeatureLevel = new FeatureLevel();
Name = name;
Type = type;
}
internal static bool TryParseFromCodecs(string line, out Codec codec)
{
Match match = _codecsFormatRegex.Match(line);
if (!match.Success)
{
codec = null;
return false;
}
string value = match.Groups[7].Value;
CodecType codecType = match.Groups[3].Value switch
{
"V" => CodecType.Video,
"A" => CodecType.Audio,
"D" => CodecType.Data,
"S" => CodecType.Subtitle,
_ => CodecType.Unknown,
};
if (codecType == CodecType.Unknown)
{
codec = null;
return false;
}
codec = new Codec(value, codecType);
codec.DecodingSupported = match.Groups[1].Value != ".";
codec.EncodingSupported = match.Groups[2].Value != ".";
codec.IsIntraFrameOnly = match.Groups[4].Value != ".";
codec.IsLossy = match.Groups[5].Value != ".";
codec.IsLossless = match.Groups[6].Value != ".";
codec.Description = m