mirror of
https://github.com/rosenbjerg/FFMpegCore.git
synced 2024-11-10 08:34:12 +01:00
parent
e0b7d652d9
commit
152683323e
47 changed files with 485 additions and 410 deletions
|
@ -2,7 +2,8 @@
|
|||
using System;
|
||||
using FFMpegCore.Arguments;
|
||||
using FFMpegCore.Enums;
|
||||
using FFMpegCore.Exceptions;
|
||||
using FFMpegCore.Models;
|
||||
using FFMpegCore.Utils;
|
||||
|
||||
namespace FFMpegCore.Test
|
||||
{
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
using System;
|
||||
using FFMpegCore.Enums;
|
||||
using FFMpegCore.Test.Resources;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using System.IO;
|
||||
using FFMpegCore.Utils;
|
||||
|
||||
namespace FFMpegCore.Test
|
||||
{
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
using FFMpegCore.Exceptions;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using FFMpegCore.Models;
|
||||
|
||||
namespace FFMpegCore.Test
|
||||
{
|
||||
|
@ -12,33 +9,33 @@ public class PixelFormatTests
|
|||
[TestMethod]
|
||||
public void PixelFormats_Enumerate()
|
||||
{
|
||||
var formats = FFMpeg.GetPixelFormats();
|
||||
var formats = FFMpegUtils.GetPixelFormats();
|
||||
Assert.IsTrue(formats.Count > 0);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void PixelFormats_TryGetExisting()
|
||||
{
|
||||
Assert.IsTrue(FFMpeg.TryGetPixelFormat("yuv420p", out _));
|
||||
Assert.IsTrue(FFMpegUtils.TryGetPixelFormat("yuv420p", out _));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void PixelFormats_TryGetNotExisting()
|
||||
{
|
||||
Assert.IsFalse(FFMpeg.TryGetPixelFormat("yuv420pppUnknown", out _));
|
||||
Assert.IsFalse(FFMpegUtils.TryGetPixelFormat("yuv420pppUnknown", out _));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void PixelFormats_GetExisting()
|
||||
{
|
||||
var fmt = FFMpeg.GetPixelFormat("yuv420p");
|
||||
var fmt = FFMpegUtils.GetPixelFormat("yuv420p");
|
||||
Assert.IsTrue(fmt.Components == 3 && fmt.BitsPerPixel == 12);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void PixelFormats_GetNotExisting()
|
||||
{
|
||||
Assert.ThrowsException<FFMpegException>(() => FFMpeg.GetPixelFormat("yuv420pppUnknown"));
|
||||
Assert.ThrowsException<FFMpegException>(() => FFMpegUtils.GetPixelFormat("yuv420pppUnknown"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using FFMpegCore.Enums;
|
||||
using System.IO;
|
||||
using FFMpegCore.Models;
|
||||
|
||||
namespace FFMpegCore.Test.Resources
|
||||
{
|
||||
|
|
|
@ -7,8 +7,9 @@
|
|||
using System.IO;
|
||||
using System.Linq;
|
||||
using FFMpegCore.Arguments;
|
||||
using FFMpegCore.Exceptions;
|
||||
using FFMpegCore.Models;
|
||||
using FFMpegCore.Pipes;
|
||||
using FFMpegCore.Utils;
|
||||
|
||||
namespace FFMpegCore.Test
|
||||
{
|
||||
|
@ -127,7 +128,8 @@ private void ConvertToStreamPipe(params IArgument[] inputArguments)
|
|||
|
||||
var scaling = arguments.Find<ScaleArgument>();
|
||||
|
||||
processor.ProcessSynchronously();
|
||||
var result = processor.ProcessSynchronously();
|
||||
Assert.IsTrue(result);
|
||||
|
||||
ms.Position = 0;
|
||||
var outputVideo = FFProbe.Analyse(ms);
|
||||
|
@ -157,7 +159,7 @@ private void ConvertToStreamPipe(params IArgument[] inputArguments)
|
|||
}
|
||||
}
|
||||
|
||||
public void Convert(ContainerFormat type, Action<MediaAnalysis> validationMethod, params IArgument[] inputArguments)
|
||||
private void Convert(ContainerFormat type, Action<MediaAnalysis> validationMethod, params IArgument[] inputArguments)
|
||||
{
|
||||
var output = Input.OutputLocation(type);
|
||||
|
||||
|
@ -207,12 +209,7 @@ public void Convert(ContainerFormat type, Action<MediaAnalysis> validationMethod
|
|||
}
|
||||
}
|
||||
|
||||
public void Convert(ContainerFormat type, params IArgument[] inputArguments)
|
||||
{
|
||||
Convert(type, null, inputArguments);
|
||||
}
|
||||
|
||||
public void ConvertFromPipe(ContainerFormat type, System.Drawing.Imaging.PixelFormat fmt, params IArgument[] inputArguments)
|
||||
private void ConvertFromPipe(ContainerFormat type, System.Drawing.Imaging.PixelFormat fmt, params IArgument[] inputArguments)
|
||||
{
|
||||
var output = Input.OutputLocation(type);
|
||||
|
||||
|
@ -276,7 +273,7 @@ public void Video_ToMP4_YUV444p()
|
|||
[TestMethod]
|
||||
public void Video_ToMP4_Args()
|
||||
{
|
||||
Convert(VideoType.Mp4, new VideoCodecArgument(VideoCodec.LibX264));
|
||||
Convert(VideoType.Mp4, null, new VideoCodecArgument(VideoCodec.LibX264));
|
||||
}
|
||||
|
||||
[DataTestMethod]
|
||||
|
@ -321,13 +318,14 @@ public void Video_ToMP4_Args_StreamOutputPipe_Async()
|
|||
{
|
||||
using var ms = new MemoryStream();
|
||||
var pipeSource = new StreamPipeDataReader(ms);
|
||||
FFMpegArguments
|
||||
var result = FFMpegArguments
|
||||
.FromInputFiles(VideoLibrary.LocalVideo)
|
||||
.WithVideoCodec(VideoCodec.LibX264)
|
||||
.ForceFormat("matroska")
|
||||
.OutputToPipe(pipeSource)
|
||||
.ProcessAsynchronously()
|
||||
.WaitForResult();
|
||||
Assert.IsTrue(result);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
|
@ -345,7 +343,7 @@ public void Video_ToTS()
|
|||
[TestMethod]
|
||||
public void Video_ToTS_Args()
|
||||
{
|
||||
Convert(VideoType.Ts,
|
||||
Convert(VideoType.Ts, null,
|
||||
new CopyArgument(),
|
||||
new BitStreamFilterArgument(Channel.Video, Filter.H264_Mp4ToAnnexB),
|
||||
new ForceFormatArgument(VideoType.MpegTs));
|
||||
|
@ -369,7 +367,7 @@ public void Video_ToOGV_Resize()
|
|||
[TestMethod]
|
||||
public void Video_ToOGV_Resize_Args()
|
||||
{
|
||||
Convert(VideoType.Ogv, new ScaleArgument(VideoSize.Ed), new VideoCodecArgument(VideoCodec.LibTheora));
|
||||
Convert(VideoType.Ogv, null, new ScaleArgument(VideoSize.Ed), new VideoCodecArgument(VideoCodec.LibTheora));
|
||||
}
|
||||
|
||||
[DataTestMethod]
|
||||
|
@ -390,7 +388,7 @@ public void Video_ToMP4_Resize()
|
|||
[TestMethod]
|
||||
public void Video_ToMP4_Resize_Args()
|
||||
{
|
||||
Convert(VideoType.Mp4, new ScaleArgument(VideoSize.Ld), new VideoCodecArgument(VideoCodec.LibX264));
|
||||
Convert(VideoType.Mp4, null, new ScaleArgument(VideoSize.Ld), new VideoCodecArgument(VideoCodec.LibX264));
|
||||
}
|
||||
|
||||
[DataTestMethod]
|
||||
|
@ -547,7 +545,6 @@ public void Video_With_Only_Audio_Should_Extract_Metadata()
|
|||
Assert.AreEqual(null, video.PrimaryVideoStream);
|
||||
Assert.AreEqual("aac", video.PrimaryAudioStream.CodecName);
|
||||
Assert.AreEqual(79.5, video.Duration.TotalSeconds, 0.5);
|
||||
// Assert.AreEqual(1.25, video.Size);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
|
@ -621,12 +618,13 @@ public void Video_TranscodeInMemory()
|
|||
var reader = new StreamPipeDataReader(resStream);
|
||||
var writer = new RawVideoPipeDataWriter(BitmapSource.CreateBitmaps(128, System.Drawing.Imaging.PixelFormat.Format24bppRgb, 128, 128));
|
||||
|
||||
FFMpegArguments
|
||||
var result = FFMpegArguments
|
||||
.FromPipe(writer)
|
||||
.WithVideoCodec("vp9")
|
||||
.ForceFormat("webm")
|
||||
.OutputToPipe(reader)
|
||||
.ProcessSynchronously();
|
||||
Assert.IsTrue(result);
|
||||
|
||||
resStream.Position = 0;
|
||||
var vi = FFProbe.Analyse(resStream);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
using FFMpegCore.Enums;
|
||||
using FFMpegCore.Exceptions;
|
||||
using FFMpegCore.Models;
|
||||
|
||||
namespace FFMpegCore.Arguments
|
||||
{
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
using FFMpegCore.Enums;
|
||||
using FFMpegCore.Exceptions;
|
||||
using FFMpegCore.Models;
|
||||
|
||||
namespace FFMpegCore.Arguments
|
||||
{
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
using FFMpegCore.Enums;
|
||||
using FFMpegCore.Models;
|
||||
|
||||
namespace FFMpegCore.Arguments
|
||||
{
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
using FFMpegCore.Enums;
|
||||
using FFMpegCore.Models;
|
||||
|
||||
namespace FFMpegCore.Arguments
|
||||
{
|
||||
|
|
|
@ -50,7 +50,7 @@ public interface IArgument
|
|||
public interface IInputOutputArgument : IArgument
|
||||
{
|
||||
void Pre() {}
|
||||
Task During(CancellationToken? cancellationToken = null) => Task.CompletedTask;
|
||||
Task During(CancellationToken cancellationToken) => Task.CompletedTask;
|
||||
void Post() {}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using FFMpegCore.Exceptions;
|
||||
using FFMpegCore.Models;
|
||||
|
||||
namespace FFMpegCore.Arguments
|
||||
{
|
||||
|
@ -11,11 +11,13 @@ public class OutputArgument : IOutputArgument
|
|||
{
|
||||
public readonly string Path;
|
||||
public readonly bool Overwrite;
|
||||
public readonly bool VerifyOutputExists;
|
||||
|
||||
public OutputArgument(string path, bool overwrite = false)
|
||||
public OutputArgument(string path, bool overwrite = false, bool verifyOutputExists = true)
|
||||
{
|
||||
Path = path;
|
||||
Overwrite = overwrite;
|
||||
VerifyOutputExists = verifyOutputExists;
|
||||
}
|
||||
|
||||
public void Pre()
|
||||
|
@ -25,7 +27,7 @@ public void Pre()
|
|||
}
|
||||
public void Post()
|
||||
{
|
||||
if (!File.Exists(Path))
|
||||
if (VerifyOutputExists && !File.Exists(Path))
|
||||
throw new FFMpegException(FFMpegExceptionType.File, "Output file was not created");
|
||||
}
|
||||
|
||||
|
|
|
@ -34,16 +34,15 @@ public void Post()
|
|||
Pipe = null!;
|
||||
}
|
||||
|
||||
public async Task During(CancellationToken? cancellationToken = null)
|
||||
public async Task During(CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
await ProcessDataAsync(cancellationToken ?? CancellationToken.None).ConfigureAwait(false);
|
||||
await ProcessDataAsync(cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
}
|
||||
Post();
|
||||
}
|
||||
|
||||
public abstract Task ProcessDataAsync(CancellationToken token);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
using FFMpegCore.Enums;
|
||||
using FFMpegCore.Exceptions;
|
||||
using FFMpegCore.Models;
|
||||
|
||||
namespace FFMpegCore.Arguments
|
||||
{
|
||||
|
|
9
FFMpegCore/FFMpeg/Enums/Channel.cs
Normal file
9
FFMpegCore/FFMpeg/Enums/Channel.cs
Normal file
|
@ -0,0 +1,9 @@
|
|||
namespace FFMpegCore.Enums
|
||||
{
|
||||
public enum Channel
|
||||
{
|
||||
Audio,
|
||||
Video,
|
||||
Both
|
||||
}
|
||||
}
|
14
FFMpegCore/FFMpeg/Enums/CodecType.cs
Normal file
14
FFMpegCore/FFMpeg/Enums/CodecType.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
using System;
|
||||
|
||||
namespace FFMpegCore.Enums
|
||||
{
|
||||
[Flags]
|
||||
public enum CodecType
|
||||
{
|
||||
Unknown = 0,
|
||||
Video = 1 << 1,
|
||||
Audio = 1 << 2,
|
||||
Subtitle = 1 << 3,
|
||||
Data = 1 << 4,
|
||||
}
|
||||
}
|
|
@ -1,54 +0,0 @@
|
|||
namespace FFMpegCore.Enums
|
||||
{
|
||||
public enum CodecType
|
||||
{
|
||||
Unknown = 0,
|
||||
Video = 1 << 1,
|
||||
Audio = 1 << 2,
|
||||
Subtitle = 1 << 3,
|
||||
Data = 1 << 4,
|
||||
}
|
||||
|
||||
public static class VideoCodec
|
||||
{
|
||||
public static Codec LibX264 => FFMpeg.GetCodec("libx264");
|
||||
public static Codec LibVpx => FFMpeg.GetCodec("libvpx");
|
||||
public static Codec LibTheora => FFMpeg.GetCodec("libtheora");
|
||||
public static Codec Png => FFMpeg.GetCodec("png");
|
||||
public static Codec MpegTs => FFMpeg.GetCodec("mpegts");
|
||||
}
|
||||
|
||||
public static class AudioCodec
|
||||
{
|
||||
public static Codec Aac => FFMpeg.GetCodec("aac");
|
||||
public static Codec LibVorbis => FFMpeg.GetCodec("libvorbis");
|
||||
public static Codec LibFdk_Aac => FFMpeg.GetCodec("libfdk_aac");
|
||||
public static Codec Ac3 => FFMpeg.GetCodec("ac3");
|
||||
public static Codec Eac3 => FFMpeg.GetCodec("eac3");
|
||||
public static Codec LibMp3Lame => FFMpeg.GetCodec("libmp3lame");
|
||||
}
|
||||
|
||||
public static class VideoType
|
||||
{
|
||||
public static ContainerFormat MpegTs => FFMpeg.GetContinerFormat("mpegts");
|
||||
public static ContainerFormat Ts => FFMpeg.GetContinerFormat("mpegts");
|
||||
public static ContainerFormat Mp4 => FFMpeg.GetContinerFormat("mp4");
|
||||
public static ContainerFormat Mov => FFMpeg.GetContinerFormat("mov");
|
||||
public static ContainerFormat Avi => FFMpeg.GetContinerFormat("avi");
|
||||
public static ContainerFormat Ogv => FFMpeg.GetContinerFormat("ogv");
|
||||
public static ContainerFormat WebM => FFMpeg.GetContinerFormat("webm");
|
||||
}
|
||||
|
||||
public enum Filter
|
||||
{
|
||||
H264_Mp4ToAnnexB,
|
||||
Aac_AdtstoAsc
|
||||
}
|
||||
|
||||
public enum Channel
|
||||
{
|
||||
Audio,
|
||||
Video,
|
||||
Both
|
||||
}
|
||||
}
|
9
FFMpegCore/FFMpeg/Enums/FeatureStatus.cs
Normal file
9
FFMpegCore/FFMpeg/Enums/FeatureStatus.cs
Normal file
|
@ -0,0 +1,9 @@
|
|||
namespace FFMpegCore.Enums
|
||||
{
|
||||
public enum FeatureStatus
|
||||
{
|
||||
Unknown,
|
||||
NotSupported,
|
||||
Supported,
|
||||
}
|
||||
}
|
8
FFMpegCore/FFMpeg/Enums/Filter.cs
Normal file
8
FFMpegCore/FFMpeg/Enums/Filter.cs
Normal file
|
@ -0,0 +1,8 @@
|
|||
namespace FFMpegCore.Enums
|
||||
{
|
||||
public enum Filter
|
||||
{
|
||||
H264_Mp4ToAnnexB,
|
||||
Aac_AdtstoAsc
|
||||
}
|
||||
}
|
|
@ -5,12 +5,13 @@
|
|||
using System.IO;
|
||||
using System.Linq;
|
||||
using FFMpegCore.Enums;
|
||||
using FFMpegCore.Exceptions;
|
||||
using FFMpegCore.Helpers;
|
||||
using FFMpegCore.Models;
|
||||
using FFMpegCore.Utils;
|
||||
|
||||
namespace FFMpegCore
|
||||
{
|
||||
public static class FFMpeg
|
||||
public static partial class FFMpeg
|
||||
{
|
||||
/// <summary>
|
||||
/// Saves a 'png' thumbnail from the input video.
|
||||
|
@ -324,178 +325,7 @@ public static bool ReplaceAudio(string input, string inputAudio, string output,
|
|||
.ProcessSynchronously();
|
||||
}
|
||||
|
||||
#region PixelFormats
|
||||
internal static IReadOnlyList<Enums.PixelFormat> GetPixelFormatsInternal()
|
||||
{
|
||||
FFMpegHelper.RootExceptionCheck(FFMpegOptions.Options.RootDirectory);
|
||||
|
||||
var list = new List<Enums.PixelFormat>();
|
||||
using var instance = new Instances.Instance(FFMpegOptions.Options.FFmpegBinary(), "-pix_fmts");
|
||||
instance.DataReceived += (e, args) =>
|
||||
{
|
||||
if (Enums.PixelFormat.TryParse(args.Data, out var fmt))
|
||||
list.Add(fmt);
|
||||
};
|
||||
|
||||
var exitCode = instance.BlockUntilFinished();
|
||||
if (exitCode != 0) throw new FFMpegException(FFMpegExceptionType.Process, string.Join("\r\n", instance.OutputData));
|
||||
|
||||
return list.AsReadOnly();
|
||||
}
|
||||
|
||||
public static IReadOnlyList<Enums.PixelFormat> GetPixelFormats()
|
||||
{
|
||||
if (!FFMpegOptions.Options.UseCache)
|
||||
return GetPixelFormatsInternal();
|
||||
return FFMpegCache.PixelFormats.Values.ToList().AsReadOnly();
|
||||
}
|
||||
|
||||
public static bool TryGetPixelFormat(string name, out Enums.PixelFormat fmt)
|
||||
{
|
||||
if (!FFMpegOptions.Options.UseCache)
|
||||
{
|
||||
fmt = GetPixelFormatsInternal().FirstOrDefault(x => x.Name == name.ToLowerInvariant().Trim());
|
||||
return fmt != null;
|
||||
}
|
||||
else
|
||||
return FFMpegCache.PixelFormats.TryGetValue(name, out fmt);
|
||||
}
|
||||
|
||||
public static Enums.PixelFormat GetPixelFormat(string name)
|
||||
{
|
||||
if (TryGetPixelFormat(name, out var fmt))
|
||||
return fmt;
|
||||
throw new FFMpegException(FFMpegExceptionType.Operation, $"Pixel format \"{name}\" not supported");
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Codecs
|
||||
internal static void ParsePartOfCodecs(Dictionary<string, Codec> codecs, string arguments, Func<string, Codec?> parser)
|
||||
{
|
||||
FFMpegHelper.RootExceptionCheck(FFMpegOptions.Options.RootDirectory);
|
||||
|
||||
using var instance = new Instances.Instance(FFMpegOptions.Options.FFmpegBinary(), arguments);
|
||||
instance.DataReceived += (e, args) =>
|
||||
{
|
||||
var codec = parser(args.Data);
|
||||
if(codec != null)
|
||||
if (codecs.TryGetValue(codec.Name, out var parentCodec))
|
||||
parentCodec.Merge(codec);
|
||||
else
|
||||
codecs.Add(codec.Name, codec);
|
||||
};
|
||||
|
||||
var exitCode = instance.BlockUntilFinished();
|
||||
if (exitCode != 0) throw new FFMpegException(FFMpegExceptionType.Process, string.Join("\r\n", instance.OutputData));
|
||||
}
|
||||
|
||||
internal static Dictionary<string, Codec> GetCodecsInternal()
|
||||
{
|
||||
var res = new Dictionary<string, Codec>();
|
||||
ParsePartOfCodecs(res, "-codecs", (s) =>
|
||||
{
|
||||
if (Codec.TryParseFromCodecs(s, out var codec))
|
||||
return codec;
|
||||
return null;
|
||||
});
|
||||
ParsePartOfCodecs(res, "-encoders", (s) =>
|
||||
{
|
||||
if (Codec.TryParseFromEncodersDecoders(s, out var codec, true))
|
||||
return codec;
|
||||
return null;
|
||||
});
|
||||
ParsePartOfCodecs(res, "-decoders", (s) =>
|
||||
{
|
||||
if (Codec.TryParseFromEncodersDecoders(s, out var codec, false))
|
||||
return codec;
|
||||
return null;
|
||||
});
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
public static IReadOnlyList<Codec> GetCodecs()
|
||||
{
|
||||
if (!FFMpegOptions.Options.UseCache)
|
||||
return GetCodecsInternal().Values.ToList().AsReadOnly();
|
||||
return FFMpegCache.Codecs.Values.ToList().AsReadOnly();
|
||||
}
|
||||
|
||||
public static IReadOnlyList<Codec> GetCodecs(CodecType type)
|
||||
{
|
||||
if (!FFMpegOptions.Options.UseCache)
|
||||
return GetCodecsInternal().Values.Where(x => x.Type == type).ToList().AsReadOnly();
|
||||
return FFMpegCache.Codecs.Values.Where(x=>x.Type == type).ToList().AsReadOnly();
|
||||
}
|
||||
|
||||
public static IReadOnlyList<Codec> GetVideoCodecs() => GetCodecs(CodecType.Video);
|
||||
public static IReadOnlyList<Codec> GetAudioCodecs() => GetCodecs(CodecType.Audio);
|
||||
public static IReadOnlyList<Codec> GetSubtitleCodecs() => GetCodecs(CodecType.Subtitle);
|
||||
public static IReadOnlyList<Codec> GetDataCodecs() => GetCodecs(CodecType.Data);
|
||||
|
||||
public static bool TryGetCodec(string name, out Codec codec)
|
||||
{
|
||||
if (!FFMpegOptions.Options.UseCache)
|
||||
{
|
||||
codec = GetCodecsInternal().Values.FirstOrDefault(x => x.Name == name.ToLowerInvariant().Trim());
|
||||
return codec != null;
|
||||
}
|
||||
else
|
||||
return FFMpegCache.Codecs.TryGetValue(name, out codec);
|
||||
}
|
||||
|
||||
public static Codec GetCodec(string name)
|
||||
{
|
||||
if (TryGetCodec(name, out var codec) && codec != null)
|
||||
return codec;
|
||||
throw new FFMpegException(FFMpegExceptionType.Operation, $"Codec \"{name}\" not supported");
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region ContainerFormats
|
||||
internal static IReadOnlyList<ContainerFormat> GetContainersFormatsInternal()
|
||||
{
|
||||
FFMpegHelper.RootExceptionCheck(FFMpegOptions.Options.RootDirectory);
|
||||
|
||||
var list = new List<ContainerFormat>();
|
||||
using var instance = new Instances.Instance(FFMpegOptions.Options.FFmpegBinary(), "-formats");
|
||||
instance.DataReceived += (e, args) =>
|
||||
{
|
||||
if (ContainerFormat.TryParse(args.Data, out var fmt))
|
||||
list.Add(fmt);
|
||||
};
|
||||
|
||||
var exitCode = instance.BlockUntilFinished();
|
||||
if (exitCode != 0) throw new FFMpegException(FFMpegExceptionType.Process, string.Join("\r\n", instance.OutputData));
|
||||
|
||||
return list.AsReadOnly();
|
||||
}
|
||||
|
||||
public static IReadOnlyList<ContainerFormat> GetContainerFormats()
|
||||
{
|
||||
if (!FFMpegOptions.Options.UseCache)
|
||||
return GetContainersFormatsInternal();
|
||||
return FFMpegCache.ContainerFormats.Values.ToList().AsReadOnly();
|
||||
}
|
||||
|
||||
public static bool TryGetContainerFormat(string name, out ContainerFormat fmt)
|
||||
{
|
||||
if (!FFMpegOptions.Options.UseCache)
|
||||
{
|
||||
fmt = GetContainersFormatsInternal().FirstOrDefault(x => x.Name == name.ToLowerInvariant().Trim());
|
||||
return fmt != null;
|
||||
}
|
||||
else
|
||||
return FFMpegCache.ContainerFormats.TryGetValue(name, out fmt);
|
||||
}
|
||||
|
||||
public static ContainerFormat GetContinerFormat(string name)
|
||||
{
|
||||
if (TryGetContainerFormat(name, out var fmt))
|
||||
return fmt;
|
||||
throw new FFMpegException(FFMpegExceptionType.Operation, $"Container format \"{name}\" not supported");
|
||||
}
|
||||
#endregion
|
||||
|
||||
private static void Cleanup(IEnumerable<string> pathList)
|
||||
{
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using FFMpegCore.Exceptions;
|
||||
using FFMpegCore.Helpers;
|
||||
using FFMpegCore.Models;
|
||||
using Instances;
|
||||
|
||||
namespace FFMpegCore
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
using System.Threading.Tasks;
|
||||
using FFMpegCore.Arguments;
|
||||
using FFMpegCore.Enums;
|
||||
using FFMpegCore.Models;
|
||||
using FFMpegCore.Pipes;
|
||||
|
||||
namespace FFMpegCore
|
||||
|
@ -85,8 +86,8 @@ private FFMpegArguments(IInputArgument inputArgument)
|
|||
|
||||
public FFMpegArguments DrawText(DrawTextOptions drawTextOptions) => WithArgument(new DrawTextArgument(drawTextOptions));
|
||||
|
||||
public FFMpegArgumentProcessor OutputToFile(string file, bool overwrite = false) => ToProcessor(new OutputArgument(file, overwrite));
|
||||
public FFMpegArgumentProcessor OutputToFile(Uri uri, bool overwrite = false) => ToProcessor(new OutputArgument(uri.AbsolutePath, overwrite));
|
||||
public FFMpegArgumentProcessor OutputToFile(string file, bool overwrite = false, bool verifyOutputExists = true) => ToProcessor(new OutputArgument(file, overwrite, verifyOutputExists));
|
||||
public FFMpegArgumentProcessor OutputToFile(Uri uri, bool overwrite = false, bool verifyOutputExists = true) => ToProcessor(new OutputArgument(uri.AbsolutePath, overwrite, verifyOutputExists));
|
||||
public FFMpegArgumentProcessor OutputToPipe(IPipeDataReader reader) => ToProcessor(new OutputPipeArgument(reader));
|
||||
|
||||
public FFMpegArguments WithArgument(IArgument argument)
|
||||
|
@ -106,7 +107,7 @@ internal void Pre()
|
|||
_inputArgument.Pre();
|
||||
_outputArgument.Pre();
|
||||
}
|
||||
internal async Task During(CancellationToken? cancellationToken = null)
|
||||
internal async Task During(CancellationToken cancellationToken)
|
||||
{
|
||||
await Task.WhenAll(_inputArgument.During(cancellationToken), _outputArgument.During(cancellationToken)).ConfigureAwait(false);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
using FFMpegCore.Enums;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using FFMpegCore.Models;
|
||||
|
||||
namespace FFMpegCore
|
||||
{
|
||||
|
@ -18,7 +18,7 @@ public static IReadOnlyDictionary<string, PixelFormat> PixelFormats
|
|||
if (_pixelFormats == null) //First check not thread safe
|
||||
lock (_syncObject)
|
||||
if (_pixelFormats == null)//Second check thread safe
|
||||
_pixelFormats = FFMpeg.GetPixelFormatsInternal().ToDictionary(x => x.Name);
|
||||
_pixelFormats = FFMpegUtils.GetPixelFormatsInternal().ToDictionary(x => x.Name);
|
||||
|
||||
return _pixelFormats;
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ public static IReadOnlyDictionary<string, Codec> Codecs
|
|||
if (_codecs == null) //First check not thread safe
|
||||
lock (_syncObject)
|
||||
if (_codecs == null)//Second check thread safe
|
||||
_codecs = FFMpeg.GetCodecsInternal();
|
||||
_codecs = FFMpegUtils.GetCodecsInternal();
|
||||
|
||||
return _codecs;
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ public static IReadOnlyDictionary<string, ContainerFormat> ContainerFormats
|
|||
if (_containers == null) //First check not thread safe
|
||||
lock (_syncObject)
|
||||
if (_containers == null)//Second check thread safe
|
||||
_containers = FFMpeg.GetContainersFormatsInternal().ToDictionary(x => x.Name);
|
||||
_containers = FFMpegUtils.GetContainersFormatsInternal().ToDictionary(x => x.Name);
|
||||
|
||||
return _containers;
|
||||
}
|
||||
|
|
189
FFMpegCore/FFMpeg/FFMpegUtils.cs
Normal file
189
FFMpegCore/FFMpeg/FFMpegUtils.cs
Normal file
|
@ -0,0 +1,189 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using FFMpegCore.Enums;
|
||||
using FFMpegCore.Helpers;
|
||||
using FFMpegCore.Models;
|
||||
|
||||
namespace FFMpegCore
|
||||
{
|
||||
public static class FFMpegUtils
|
||||
{
|
||||
#region PixelFormats
|
||||
internal static IReadOnlyList<PixelFormat> GetPixelFormatsInternal()
|
||||
{
|
||||
FFMpegHelper.RootExceptionCheck(FFMpegOptions.Options.RootDirectory);
|
||||
|
||||
var list = new List<PixelFormat>();
|
||||
using var instance = new Instances.Instance(FFMpegOptions.Options.FFmpegBinary(), "-pix_fmts");
|
||||
instance.DataReceived += (e, args) =>
|
||||
{
|
||||
if (PixelFormat.TryParse(args.Data, out var fmt))
|
||||
list.Add(fmt);
|
||||
};
|
||||
|
||||
var exitCode = instance.BlockUntilFinished();
|
||||
if (exitCode != 0) throw new FFMpegException(FFMpegExceptionType.Process, string.Join("\r\n", instance.OutputData));
|
||||
|
||||
return list.AsReadOnly();
|
||||
}
|
||||
|
||||
public static IReadOnlyList<PixelFormat> GetPixelFormats()
|
||||
{
|
||||
if (!FFMpegOptions.Options.UseCache)
|
||||
return GetPixelFormatsInternal();
|
||||
return FFMpegCache.PixelFormats.Values.ToList().AsReadOnly();
|
||||
}
|
||||
|
||||
public static bool TryGetPixelFormat(string name, out PixelFormat fmt)
|
||||
{
|
||||
if (!FFMpegOptions.Options.UseCache)
|
||||
{
|
||||
fmt = GetPixelFormatsInternal().FirstOrDefault(x => x.Name == name.ToLowerInvariant().Trim());
|
||||
return fmt != null;
|
||||
}
|
||||
else
|
||||
return FFMpegCache.PixelFormats.TryGetValue(name, out fmt);
|
||||
}
|
||||
|
||||
public static PixelFormat GetPixelFormat(string name)
|
||||
{
|
||||
if (TryGetPixelFormat(name, out var fmt))
|
||||
return fmt;
|
||||
throw new FFMpegException(FFMpegExceptionType.Operation, $"Pixel format \"{name}\" not supported");
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region ContainerFormats
|
||||
internal static IReadOnlyList<ContainerFormat> GetContainersFormatsInternal()
|
||||
{
|
||||
FFMpegHelper.RootExceptionCheck(FFMpegOptions.Options.RootDirectory);
|
||||
|
||||
var list = new List<ContainerFormat>();
|
||||
using var instance = new Instances.Instance(FFMpegOptions.Options.FFmpegBinary(), "-formats");
|
||||
instance.DataReceived += (e, args) =>
|
||||
{
|
||||
if (ContainerFormat.TryParse(args.Data, out var fmt))
|
||||
list.Add(fmt);
|
||||
};
|
||||
|
||||
var exitCode = instance.BlockUntilFinished();
|
||||
if (exitCode != 0) throw new FFMpegException(FFMpegExceptionType.Process, string.Join("\r\n", instance.OutputData));
|
||||
|
||||
return list.AsReadOnly();
|
||||
}
|
||||
|
||||
public static IReadOnlyList<ContainerFormat> GetContainerFormats()
|
||||
{
|
||||
if (!FFMpegOptions.Options.UseCache)
|
||||
return GetContainersFormatsInternal();
|
||||
return FFMpegCache.ContainerFormats.Values.ToList().AsReadOnly();
|
||||
}
|
||||
|
||||
public static bool TryGetContainerFormat(string name, out ContainerFormat fmt)
|
||||
{
|
||||
if (!FFMpegOptions.Options.UseCache)
|
||||
{
|
||||
fmt = GetContainersFormatsInternal().FirstOrDefault(x => x.Name == name.ToLowerInvariant().Trim());
|
||||
return fmt != null;
|
||||
}
|
||||
else
|
||||
return FFMpegCache.ContainerFormats.TryGetValue(name, out fmt);
|
||||
}
|
||||
|
||||
public static ContainerFormat GetContainerFormat(string name)
|
||||
{
|
||||
if (TryGetContainerFormat(name, out var fmt))
|
||||
return fmt;
|
||||
throw new FFMpegException(FFMpegExceptionType.Operation, $"Container format \"{name}\" not supported");
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Codecs
|
||||
|
||||
internal static void ParsePartOfCodecs(Dictionary<string, Codec> codecs, string arguments,
|
||||
Func<string, Codec?> parser)
|
||||
{
|
||||
FFMpegHelper.RootExceptionCheck(FFMpegOptions.Options.RootDirectory);
|
||||
|
||||
using var instance = new Instances.Instance(FFMpegOptions.Options.FFmpegBinary(), arguments);
|
||||
instance.DataReceived += (e, args) =>
|
||||
{
|
||||
var codec = parser(args.Data);
|
||||
if (codec != null)
|
||||
if (codecs.TryGetValue(codec.Name, out var parentCodec))
|
||||
parentCodec.Merge(codec);
|
||||
else
|
||||
codecs.Add(codec.Name, codec);
|
||||
};
|
||||
|
||||
var exitCode = instance.BlockUntilFinished();
|
||||
if (exitCode != 0)
|
||||
throw new FFMpegException(FFMpegExceptionType.Process, string.Join("\r\n", instance.OutputData));
|
||||
}
|
||||
|
||||
internal static Dictionary<string, Codec> GetCodecsInternal()
|
||||
{
|
||||
var res = new Dictionary<string, Codec>();
|
||||
ParsePartOfCodecs(res, "-codecs", (s) =>
|
||||
{
|
||||
if (Codec.TryParseFromCodecs(s, out var codec))
|
||||
return codec;
|
||||
return null;
|
||||
});
|
||||
ParsePartOfCodecs(res, "-encoders", (s) =>
|
||||
{
|
||||
if (Codec.TryParseFromEncodersDecoders(s, out var codec, true))
|
||||
return codec;
|
||||
return null;
|
||||
});
|
||||
ParsePartOfCodecs(res, "-decoders", (s) =>
|
||||
{
|
||||
if (Codec.TryParseFromEncodersDecoders(s, out var codec, false))
|
||||
return codec;
|
||||
return null;
|
||||
});
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
public static IReadOnlyList<Codec> GetCodecs()
|
||||
{
|
||||
if (!FFMpegOptions.Options.UseCache)
|
||||
return GetCodecsInternal().Values.ToList().AsReadOnly();
|
||||
return FFMpegCache.Codecs.Values.ToList().AsReadOnly();
|
||||
}
|
||||
|
||||
public static IReadOnlyList<Codec> GetCodecs(CodecType type)
|
||||
{
|
||||
if (!FFMpegOptions.Options.UseCache)
|
||||
return GetCodecsInternal().Values.Where(x => x.Type == type).ToList().AsReadOnly();
|
||||
return FFMpegCache.Codecs.Values.Where(x => x.Type == type).ToList().AsReadOnly();
|
||||
}
|
||||
|
||||
public static IReadOnlyList<Codec> GetVideoCodecs() => GetCodecs(CodecType.Video);
|
||||
public static IReadOnlyList<Codec> GetAudioCodecs() => GetCodecs(CodecType.Audio);
|
||||
public static IReadOnlyList<Codec> GetSubtitleCodecs() => GetCodecs(CodecType.Subtitle);
|
||||
public static IReadOnlyList<Codec> GetDataCodecs() => GetCodecs(CodecType.Data);
|
||||
|
||||
public static bool TryGetCodec(string name, out Codec codec)
|
||||
{
|
||||
if (!FFMpegOptions.Options.UseCache)
|
||||
{
|
||||
codec = GetCodecsInternal().Values.FirstOrDefault(x => x.Name == name.ToLowerInvariant().Trim());
|
||||
return codec != null;
|
||||
}
|
||||
else
|
||||
return FFMpegCache.Codecs.TryGetValue(name, out codec);
|
||||
}
|
||||
|
||||
public static Codec GetCodec(string name)
|
||||
{
|
||||
if (TryGetCodec(name, out var codec) && codec != null)
|
||||
return codec;
|
||||
throw new FFMpegException(FFMpegExceptionType.Operation, $"Codec \"{name}\" not supported");
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -1,22 +1,13 @@
|
|||
using FFMpegCore.Exceptions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System;
|
||||
using System.Text.RegularExpressions;
|
||||
using FFMpegCore.Enums;
|
||||
|
||||
namespace FFMpegCore.Enums
|
||||
namespace FFMpegCore.Models
|
||||
{
|
||||
public enum FeatureStatus
|
||||
{
|
||||
Unknown,
|
||||
NotSupported,
|
||||
Supported,
|
||||
}
|
||||
|
||||
public class Codec
|
||||
{
|
||||
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+(.+)");
|
||||
private static readonly Regex CodecsFormatRegex = new Regex(@"([D\.])([E\.])([VASD\.])([I\.])([L\.])([S\.])\s+([a-z0-9_-]+)\s+(.+)", RegexOptions.Compiled);
|
||||
private static readonly Regex DecodersEncodersFormatRegex = new Regex(@"([VASD\.])([F\.])([S\.])([X\.])([B\.])([D\.])\s+([a-z0-9_-]+)\s+(.+)", RegexOptions.Compiled);
|
||||
|
||||
public class FeatureLevel
|
||||
{
|
||||
|
@ -58,7 +49,7 @@ internal Codec(string name, CodecType type)
|
|||
|
||||
internal static bool TryParseFromCodecs(string line, out Codec codec)
|
||||
{
|
||||
var match = _codecsFormatRegex.Match(line);
|
||||
var match = CodecsFormatRegex.Match(line);
|
||||
if (!match.Success)
|
||||
{
|
||||
codec = null!;
|
||||
|
@ -81,20 +72,21 @@ internal static bool TryParseFromCodecs(string line, out Codec codec)
|
|||
return false;
|
||||
}
|
||||
|
||||
codec = new Codec(name, type);
|
||||
|
||||
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 = match.Groups[8].Value;
|
||||
codec = new Codec(name, type)
|
||||
{
|
||||
DecodingSupported = match.Groups[1].Value != ".",
|
||||
EncodingSupported = match.Groups[2].Value != ".",
|
||||
IsIntraFrameOnly = match.Groups[4].Value != ".",
|
||||
IsLossy = match.Groups[5].Value != ".",
|
||||
IsLossless = match.Groups[6].Value != ".",
|
||||
Description = match.Groups[8].Value
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
internal static bool TryParseFromEncodersDecoders(string line, out Codec codec, bool isEncoder)
|
||||
{
|
||||
var match = _decodersEncodersFormatRegex.Match(line);
|
||||
var match = DecodersEncodersFormatRegex.Match(line);
|
||||
if (!match.Success)
|
||||
{
|
||||
codec = null!;
|
||||
|
@ -135,7 +127,7 @@ internal static bool TryParseFromEncodersDecoders(string line, out Codec codec,
|
|||
internal void Merge(Codec other)
|
||||
{
|
||||
if (Name != other.Name)
|
||||
throw new FFMpegException(FFMpegExceptionType.Operation, "different codecs enable to merge");
|
||||
throw new FFMpegException(FFMpegExceptionType.Operation, "different codecs unable to merge");
|
||||
|
||||
Type |= other.Type;
|
||||
DecodingSupported |= other.DecodingSupported;
|
|
@ -1,9 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace FFMpegCore.Enums
|
||||
namespace FFMpegCore.Models
|
||||
{
|
||||
public class ContainerFormat
|
||||
{
|
|
@ -1,6 +1,6 @@
|
|||
using System;
|
||||
|
||||
namespace FFMpegCore.Exceptions
|
||||
namespace FFMpegCore.Models
|
||||
{
|
||||
public enum FFMpegExceptionType
|
||||
{
|
|
@ -1,13 +1,10 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace FFMpegCore.Enums
|
||||
namespace FFMpegCore.Models
|
||||
{
|
||||
public class PixelFormat
|
||||
{
|
||||
private static readonly Regex _formatRegex = new Regex(@"([I\.])([O\.])([H\.])([P\.])([B\.])\s+(\S+)\s+([0-9]+)\s+([0-9]+)");
|
||||
private static readonly Regex FormatRegex = new Regex(@"([I\.])([O\.])([H\.])([P\.])([B\.])\s+(\S+)\s+([0-9]+)\s+([0-9]+)", RegexOptions.Compiled);
|
||||
|
||||
public bool InputConversionSupported { get; private set; }
|
||||
public bool OutputConversionSupported { get; private set; }
|
||||
|
@ -30,7 +27,7 @@ internal PixelFormat(string name)
|
|||
|
||||
internal static bool TryParse(string line, out PixelFormat fmt)
|
||||
{
|
||||
var match = _formatRegex.Match(line);
|
||||
var match = FormatRegex.Match(line);
|
||||
if (!match.Success)
|
||||
{
|
||||
fmt = null!;
|
|
@ -2,7 +2,7 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using FFMpegCore.Exceptions;
|
||||
using FFMpegCore.Models;
|
||||
|
||||
namespace FFMpegCore.Pipes
|
||||
{
|
||||
|
|
14
FFMpegCore/FFMpeg/Utils/AudioCodec.cs
Normal file
14
FFMpegCore/FFMpeg/Utils/AudioCodec.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
using FFMpegCore.Models;
|
||||
|
||||
namespace FFMpegCore.Utils
|
||||
{
|
||||
public static class AudioCodec
|
||||
{
|
||||
public static Codec Aac => FFMpegUtils.GetCodec("aac");
|
||||
public static Codec LibVorbis => FFMpegUtils.GetCodec("libvorbis");
|
||||
public static Codec LibFdkAac => FFMpegUtils.GetCodec("libfdk_aac");
|
||||
public static Codec Ac3 => FFMpegUtils.GetCodec("ac3");
|
||||
public static Codec Eac3 => FFMpegUtils.GetCodec("eac3");
|
||||
public static Codec LibMp3Lame => FFMpegUtils.GetCodec("libmp3lame");
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
using FFMpegCore.Models;
|
||||
|
||||
namespace FFMpegCore.Enums
|
||||
namespace FFMpegCore.Utils
|
||||
{
|
||||
public static class FileExtension
|
||||
{
|
13
FFMpegCore/FFMpeg/Utils/VideoCodec.cs
Normal file
13
FFMpegCore/FFMpeg/Utils/VideoCodec.cs
Normal file
|
@ -0,0 +1,13 @@
|
|||
using FFMpegCore.Models;
|
||||
|
||||
namespace FFMpegCore.Utils
|
||||
{
|
||||
public static class VideoCodec
|
||||
{
|
||||
public static Codec LibX264 => FFMpegUtils.GetCodec("libx264");
|
||||
public static Codec LibVpx => FFMpegUtils.GetCodec("libvpx");
|
||||
public static Codec LibTheora => FFMpegUtils.GetCodec("libtheora");
|
||||
public static Codec Png => FFMpegUtils.GetCodec("png");
|
||||
public static Codec MpegTs => FFMpegUtils.GetCodec("mpegts");
|
||||
}
|
||||
}
|
15
FFMpegCore/FFMpeg/Utils/VideoType.cs
Normal file
15
FFMpegCore/FFMpeg/Utils/VideoType.cs
Normal file
|
@ -0,0 +1,15 @@
|
|||
using FFMpegCore.Models;
|
||||
|
||||
namespace FFMpegCore.Utils
|
||||
{
|
||||
public static class VideoType
|
||||
{
|
||||
public static ContainerFormat MpegTs => FFMpegUtils.GetContainerFormat("mpegts");
|
||||
public static ContainerFormat Ts => FFMpegUtils.GetContainerFormat("mpegts");
|
||||
public static ContainerFormat Mp4 => FFMpegUtils.GetContainerFormat("mp4");
|
||||
public static ContainerFormat Mov => FFMpegUtils.GetContainerFormat("mov");
|
||||
public static ContainerFormat Avi => FFMpegUtils.GetContainerFormat("avi");
|
||||
public static ContainerFormat Ogv => FFMpegUtils.GetContainerFormat("ogv");
|
||||
public static ContainerFormat WebM => FFMpegUtils.GetContainerFormat("webm");
|
||||
}
|
||||
}
|
|
@ -32,8 +32,4 @@
|
|||
<PackageReference Include="System.Text.Json" Version="4.7.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="FFMpeg\Models\" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.IO;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using FFMpegCore.Arguments;
|
||||
using FFMpegCore.Exceptions;
|
||||
using FFMpegCore.Helpers;
|
||||
using FFMpegCore.Models;
|
||||
using FFMpegCore.Pipes;
|
||||
using Instances;
|
||||
|
||||
|
@ -28,7 +28,7 @@ public static MediaAnalysis Analyse(System.IO.Stream stream, int outputCapacity
|
|||
var task = instance.FinishedRunning();
|
||||
try
|
||||
{
|
||||
pipeArgument.During().ConfigureAwait(false).GetAwaiter().GetResult();
|
||||
pipeArgument.During(CancellationToken.None).ConfigureAwait(false).GetAwaiter().GetResult();
|
||||
}
|
||||
catch (IOException) { }
|
||||
finally
|
||||
|
@ -57,7 +57,7 @@ public static async Task<MediaAnalysis> AnalyseAsync(System.IO.Stream stream, in
|
|||
var task = instance.FinishedRunning();
|
||||
try
|
||||
{
|
||||
await pipeArgument.During();
|
||||
await pipeArgument.During(CancellationToken.None);
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
|
|
|
@ -8,67 +8,4 @@ public class FFProbeAnalysis
|
|||
[JsonPropertyName("streams")]
|
||||
public List<Stream> Streams { get; set; } = null!;
|
||||
}
|
||||
|
||||
public class Stream
|
||||
{
|
||||
[JsonPropertyName("index")]
|
||||
public int Index { get; set; }
|
||||
|
||||
[JsonPropertyName("avg_frame_rate")]
|
||||
public string AvgFrameRate { get; set; } = null!;
|
||||
|
||||
[JsonPropertyName("bits_per_raw_sample")]
|
||||
public string BitsPerRawSample { get; set; } = null!;
|
||||
|
||||
[JsonPropertyName("bit_rate")]
|
||||
public string BitRate { get; set; } = null!;
|
||||
|
||||
[JsonPropertyName("channels")]
|
||||
public int? Channels { get; set; }
|
||||
|
||||
[JsonPropertyName("channel_layout")]
|
||||
public string ChannelLayout { get; set; } = null!;
|
||||
|
||||
[JsonPropertyName("codec_type")]
|
||||
public string CodecType { get; set; } = null!;
|
||||
|
||||
[JsonPropertyName("codec_name")]
|
||||
public string CodecName { get; set; } = null!;
|
||||
|
||||
[JsonPropertyName("codec_long_name")]
|
||||
public string CodecLongName { get; set; } = null!;
|
||||
|
||||
[JsonPropertyName("display_aspect_ratio")]
|
||||
public string DisplayAspectRatio { get; set; } = null!;
|
||||
|
||||
[JsonPropertyName("duration")]
|
||||
public string Duration { get; set; } = null!;
|
||||
|
||||
[JsonPropertyName("profile")]
|
||||
public string Profile { get; set; } = null!;
|
||||
|
||||
[JsonPropertyName("width")]
|
||||
public int? Width { get; set; }
|
||||
|
||||
[JsonPropertyName("height")]
|
||||
public int? Height { get; set; }
|
||||
|
||||
[JsonPropertyName("r_frame_rate")]
|
||||
public string FrameRate { get; set; } = null!;
|
||||
|
||||
[JsonPropertyName("pix_fmt")]
|
||||
public string PixelFormat { get; set; } = null!;
|
||||
|
||||
[JsonPropertyName("sample_rate")]
|
||||
public string SampleRate { get; set; } = null!;
|
||||
|
||||
[JsonPropertyName("tags")]
|
||||
public Tags Tags { get; set; } = null!;
|
||||
}
|
||||
|
||||
public class Tags
|
||||
{
|
||||
[JsonPropertyName("DURATION")]
|
||||
public string Duration { get; set; } = null!;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,24 +10,29 @@ internal MediaAnalysis(string path, FFProbeAnalysis analysis)
|
|||
{
|
||||
VideoStreams = analysis.Streams.Where(stream => stream.CodecType == "video").Select(ParseVideoStream).ToList();
|
||||
AudioStreams = analysis.Streams.Where(stream => stream.CodecType == "audio").Select(ParseAudioStream).ToList();
|
||||
TextStreams = analysis.Streams.Where(stream => stream.CodecTagString == "text").Select(ParseTextStream).ToList();
|
||||
PrimaryVideoStream = VideoStreams.OrderBy(stream => stream.Index).FirstOrDefault();
|
||||
PrimaryAudioStream = AudioStreams.OrderBy(stream => stream.Index).FirstOrDefault();
|
||||
PrimaryTextStream = TextStreams.OrderBy(stream => stream.Index).FirstOrDefault();
|
||||
Path = path;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public string Path { get; }
|
||||
public string Extension => System.IO.Path.GetExtension(Path);
|
||||
|
||||
public TimeSpan Duration => TimeSpan.FromSeconds(Math.Max(
|
||||
PrimaryVideoStream?.Duration.TotalSeconds ?? 0,
|
||||
PrimaryAudioStream?.Duration.TotalSeconds ?? 0));
|
||||
public AudioStream PrimaryAudioStream { get; }
|
||||
|
||||
public VideoStream PrimaryVideoStream { get; }
|
||||
public AudioStream PrimaryAudioStream { get; }
|
||||
public TextStream PrimaryTextStream { get; }
|
||||
|
||||
public List<VideoStream> VideoStreams { get; }
|
||||
public List<AudioStream> AudioStreams { get; }
|
||||
public List<TextStream> TextStreams { get; set; }
|
||||
|
||||
private VideoStream ParseVideoStream(Stream stream)
|
||||
{
|
||||
|
@ -45,7 +50,8 @@ private VideoStream ParseVideoStream(Stream stream)
|
|||
Height = stream.Height!.Value,
|
||||
Width = stream.Width!.Value,
|
||||
Profile = stream.Profile,
|
||||
PixelFormat = stream.PixelFormat
|
||||
PixelFormat = stream.PixelFormat,
|
||||
Language = stream.Tags.Language
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -67,7 +73,19 @@ private AudioStream ParseAudioStream(Stream stream)
|
|||
Channels = stream.Channels ?? default,
|
||||
ChannelLayout = stream.ChannelLayout,
|
||||
Duration = TimeSpan.FromSeconds(ParseDoubleInvariant(stream.Duration ?? stream.Tags.Duration ?? "0")),
|
||||
SampleRateHz = !string.IsNullOrEmpty(stream.SampleRate) ? ParseIntInvariant(stream.SampleRate) : default
|
||||
SampleRateHz = !string.IsNullOrEmpty(stream.SampleRate) ? ParseIntInvariant(stream.SampleRate) : default,
|
||||
Language = stream.Tags.Language
|
||||
};
|
||||
}
|
||||
private TextStream ParseTextStream(Stream stream)
|
||||
{
|
||||
return new TextStream
|
||||
{
|
||||
Index = stream.Index,
|
||||
CodecName = stream.CodecName,
|
||||
CodecLongName = stream.CodecLongName,
|
||||
Duration = TimeSpan.FromSeconds(ParseDoubleInvariant(stream.Duration ?? stream.Tags.Duration ?? "0")),
|
||||
Language = stream.Tags.Language
|
||||
};
|
||||
}
|
||||
|
||||
|
|
12
FFMpegCore/FFProbe/Models/MediaStream.cs
Normal file
12
FFMpegCore/FFProbe/Models/MediaStream.cs
Normal file
|
@ -0,0 +1,12 @@
|
|||
using FFMpegCore.Models;
|
||||
|
||||
namespace FFMpegCore
|
||||
{
|
||||
public abstract class MediaStream : SimpleStream
|
||||
{
|
||||
public int BitRate { get; internal set; }
|
||||
|
||||
public Codec GetCodecInfo() => FFMpegUtils.GetCodec(CodecName);
|
||||
|
||||
}
|
||||
}
|
|
@ -1,16 +1,13 @@
|
|||
using FFMpegCore.Enums;
|
||||
using System;
|
||||
using System;
|
||||
|
||||
namespace FFMpegCore
|
||||
{
|
||||
public class MediaStream
|
||||
public abstract class SimpleStream
|
||||
{
|
||||
public int Index { get; internal set; }
|
||||
public string CodecName { get; internal set; } = null!;
|
||||
public string CodecLongName { get; internal set; } = null!;
|
||||
public int BitRate { get; internal set; }
|
||||
public TimeSpan Duration { get; internal set; }
|
||||
|
||||
public Codec GetCodecInfo() => FFMpeg.GetCodec(CodecName);
|
||||
public string? Language { get; internal set; }
|
||||
}
|
||||
}
|
7
FFMpegCore/FFProbe/Models/TextStream.cs
Normal file
7
FFMpegCore/FFProbe/Models/TextStream.cs
Normal file
|
@ -0,0 +1,7 @@
|
|||
namespace FFMpegCore
|
||||
{
|
||||
public class TextStream : SimpleStream
|
||||
{
|
||||
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
using FFMpegCore.Enums;
|
||||
using FFMpegCore.Models;
|
||||
|
||||
namespace FFMpegCore
|
||||
{
|
||||
|
@ -13,6 +13,6 @@ public class VideoStream : MediaStream
|
|||
public double FrameRate { get; internal set; }
|
||||
public string PixelFormat { get; internal set; } = null!;
|
||||
|
||||
public PixelFormat GetPixelFormatInfo() => FFMpeg.GetPixelFormat(PixelFormat);
|
||||
public PixelFormat GetPixelFormatInfo() => FFMpegUtils.GetPixelFormat(PixelFormat);
|
||||
}
|
||||
}
|
64
FFMpegCore/FFProbe/Stream.cs
Normal file
64
FFMpegCore/FFProbe/Stream.cs
Normal file
|
@ -0,0 +1,64 @@
|
|||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace FFMpegCore
|
||||
{
|
||||
public class Stream
|
||||
{
|
||||
[JsonPropertyName("index")]
|
||||
public int Index { get; set; }
|
||||
|
||||
[JsonPropertyName("avg_frame_rate")]
|
||||
public string AvgFrameRate { get; set; } = null!;
|
||||
|
||||
[JsonPropertyName("bits_per_raw_sample")]
|
||||
public string BitsPerRawSample { get; set; } = null!;
|
||||
|
||||
[JsonPropertyName("bit_rate")]
|
||||
public string BitRate { get; set; } = null!;
|
||||
|
||||
[JsonPropertyName("channels")]
|
||||
public int? Channels { get; set; }
|
||||
|
||||
[JsonPropertyName("channel_layout")]
|
||||
public string ChannelLayout { get; set; } = null!;
|
||||
|
||||
[JsonPropertyName("codec_type")]
|
||||
public string CodecType { get; set; } = null!;
|
||||
|
||||
[JsonPropertyName("codec_name")]
|
||||
public string CodecName { get; set; } = null!;
|
||||
|
||||
[JsonPropertyName("codec_long_name")]
|
||||
public string CodecLongName { get; set; } = null!;
|
||||
|
||||
[JsonPropertyName("codec_tag_string")]
|
||||
public string CodecTagString { get; set; } = null!;
|
||||
|
||||
[JsonPropertyName("display_aspect_ratio")]
|
||||
public string DisplayAspectRatio { get; set; } = null!;
|
||||
|
||||
[JsonPropertyName("duration")]
|
||||
public string Duration { get; set; } = null!;
|
||||
|
||||
[JsonPropertyName("profile")]
|
||||
public string Profile { get; set; } = null!;
|
||||
|
||||
[JsonPropertyName("width")]
|
||||
public int? Width { get; set; }
|
||||
|
||||
[JsonPropertyName("height")]
|
||||
public int? Height { get; set; }
|
||||
|
||||
[JsonPropertyName("r_frame_rate")]
|
||||
public string FrameRate { get; set; } = null!;
|
||||
|
||||
[JsonPropertyName("pix_fmt")]
|
||||
public string PixelFormat { get; set; } = null!;
|
||||
|
||||
[JsonPropertyName("sample_rate")]
|
||||
public string SampleRate { get; set; } = null!;
|
||||
|
||||
[JsonPropertyName("tags")]
|
||||
public Tags Tags { get; set; } = null!;
|
||||
}
|
||||
}
|
13
FFMpegCore/FFProbe/Tags.cs
Normal file
13
FFMpegCore/FFProbe/Tags.cs
Normal file
|
@ -0,0 +1,13 @@
|
|||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace FFMpegCore
|
||||
{
|
||||
public class Tags
|
||||
{
|
||||
[JsonPropertyName("duration")]
|
||||
public string Duration { get; set; } = null!;
|
||||
|
||||
[JsonPropertyName("language")]
|
||||
public string Language { get; set; } = null!;
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
using System;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using FFMpegCore.Exceptions;
|
||||
using FFMpegCore.Models;
|
||||
|
||||
namespace FFMpegCore.Helpers
|
||||
{
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
using FFMpegCore.Exceptions;
|
||||
using FFMpegCore.Models;
|
||||
|
||||
namespace FFMpegCore.Helpers
|
||||
{
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
using System;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using FFMpegCore.Enums;
|
||||
using FFMpegCore.Helpers;
|
||||
using FFMpegCore.Utils;
|
||||
|
||||
namespace FFMpegCore
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue