Former-commit-id: d95f687e46
This commit is contained in:
Malte Rosenbjerg 2020-05-12 21:05:00 +02:00
parent e0b7d652d9
commit 152683323e
47 changed files with 485 additions and 410 deletions

View file

@ -2,7 +2,8 @@
using System; using System;
using FFMpegCore.Arguments; using FFMpegCore.Arguments;
using FFMpegCore.Enums; using FFMpegCore.Enums;
using FFMpegCore.Exceptions; using FFMpegCore.Models;
using FFMpegCore.Utils;
namespace FFMpegCore.Test namespace FFMpegCore.Test
{ {

View file

@ -1,8 +1,8 @@
using System; using System;
using FFMpegCore.Enums;
using FFMpegCore.Test.Resources; using FFMpegCore.Test.Resources;
using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.IO; using System.IO;
using FFMpegCore.Utils;
namespace FFMpegCore.Test namespace FFMpegCore.Test
{ {

View file

@ -1,8 +1,5 @@
using FFMpegCore.Exceptions; using Microsoft.VisualStudio.TestTools.UnitTesting;
using Microsoft.VisualStudio.TestTools.UnitTesting; using FFMpegCore.Models;
using System;
using System.Collections.Generic;
using System.Text;
namespace FFMpegCore.Test namespace FFMpegCore.Test
{ {
@ -12,33 +9,33 @@ public class PixelFormatTests
[TestMethod] [TestMethod]
public void PixelFormats_Enumerate() public void PixelFormats_Enumerate()
{ {
var formats = FFMpeg.GetPixelFormats(); var formats = FFMpegUtils.GetPixelFormats();
Assert.IsTrue(formats.Count > 0); Assert.IsTrue(formats.Count > 0);
} }
[TestMethod] [TestMethod]
public void PixelFormats_TryGetExisting() public void PixelFormats_TryGetExisting()
{ {
Assert.IsTrue(FFMpeg.TryGetPixelFormat("yuv420p", out _)); Assert.IsTrue(FFMpegUtils.TryGetPixelFormat("yuv420p", out _));
} }
[TestMethod] [TestMethod]
public void PixelFormats_TryGetNotExisting() public void PixelFormats_TryGetNotExisting()
{ {
Assert.IsFalse(FFMpeg.TryGetPixelFormat("yuv420pppUnknown", out _)); Assert.IsFalse(FFMpegUtils.TryGetPixelFormat("yuv420pppUnknown", out _));
} }
[TestMethod] [TestMethod]
public void PixelFormats_GetExisting() public void PixelFormats_GetExisting()
{ {
var fmt = FFMpeg.GetPixelFormat("yuv420p"); var fmt = FFMpegUtils.GetPixelFormat("yuv420p");
Assert.IsTrue(fmt.Components == 3 && fmt.BitsPerPixel == 12); Assert.IsTrue(fmt.Components == 3 && fmt.BitsPerPixel == 12);
} }
[TestMethod] [TestMethod]
public void PixelFormats_GetNotExisting() public void PixelFormats_GetNotExisting()
{ {
Assert.ThrowsException<FFMpegException>(() => FFMpeg.GetPixelFormat("yuv420pppUnknown")); Assert.ThrowsException<FFMpegException>(() => FFMpegUtils.GetPixelFormat("yuv420pppUnknown"));
} }
} }
} }

View file

@ -1,6 +1,5 @@
using System; using System.IO;
using System.IO; using FFMpegCore.Models;
using FFMpegCore.Enums;
namespace FFMpegCore.Test.Resources namespace FFMpegCore.Test.Resources
{ {

View file

@ -7,8 +7,9 @@
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using FFMpegCore.Arguments; using FFMpegCore.Arguments;
using FFMpegCore.Exceptions; using FFMpegCore.Models;
using FFMpegCore.Pipes; using FFMpegCore.Pipes;
using FFMpegCore.Utils;
namespace FFMpegCore.Test namespace FFMpegCore.Test
{ {
@ -127,7 +128,8 @@ private void ConvertToStreamPipe(params IArgument[] inputArguments)
var scaling = arguments.Find<ScaleArgument>(); var scaling = arguments.Find<ScaleArgument>();
processor.ProcessSynchronously(); var result = processor.ProcessSynchronously();
Assert.IsTrue(result);
ms.Position = 0; ms.Position = 0;
var outputVideo = FFProbe.Analyse(ms); 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); 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) private void ConvertFromPipe(ContainerFormat type, System.Drawing.Imaging.PixelFormat fmt, params IArgument[] inputArguments)
{
Convert(type, null, inputArguments);
}
public void ConvertFromPipe(ContainerFormat type, System.Drawing.Imaging.PixelFormat fmt, params IArgument[] inputArguments)
{ {
var output = Input.OutputLocation(type); var output = Input.OutputLocation(type);
@ -276,7 +273,7 @@ public void Video_ToMP4_YUV444p()
[TestMethod] [TestMethod]
public void Video_ToMP4_Args() public void Video_ToMP4_Args()
{ {
Convert(VideoType.Mp4, new VideoCodecArgument(VideoCodec.LibX264)); Convert(VideoType.Mp4, null, new VideoCodecArgument(VideoCodec.LibX264));
} }
[DataTestMethod] [DataTestMethod]
@ -321,13 +318,14 @@ public void Video_ToMP4_Args_StreamOutputPipe_Async()
{ {
using var ms = new MemoryStream(); using var ms = new MemoryStream();
var pipeSource = new StreamPipeDataReader(ms); var pipeSource = new StreamPipeDataReader(ms);
FFMpegArguments var result = FFMpegArguments
.FromInputFiles(VideoLibrary.LocalVideo) .FromInputFiles(VideoLibrary.LocalVideo)
.WithVideoCodec(VideoCodec.LibX264) .WithVideoCodec(VideoCodec.LibX264)
.ForceFormat("matroska") .ForceFormat("matroska")
.OutputToPipe(pipeSource) .OutputToPipe(pipeSource)
.ProcessAsynchronously() .ProcessAsynchronously()
.WaitForResult(); .WaitForResult();
Assert.IsTrue(result);
} }
[TestMethod] [TestMethod]
@ -345,7 +343,7 @@ public void Video_ToTS()
[TestMethod] [TestMethod]
public void Video_ToTS_Args() public void Video_ToTS_Args()
{ {
Convert(VideoType.Ts, Convert(VideoType.Ts, null,
new CopyArgument(), new CopyArgument(),
new BitStreamFilterArgument(Channel.Video, Filter.H264_Mp4ToAnnexB), new BitStreamFilterArgument(Channel.Video, Filter.H264_Mp4ToAnnexB),
new ForceFormatArgument(VideoType.MpegTs)); new ForceFormatArgument(VideoType.MpegTs));
@ -369,7 +367,7 @@ public void Video_ToOGV_Resize()
[TestMethod] [TestMethod]
public void Video_ToOGV_Resize_Args() 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] [DataTestMethod]
@ -390,7 +388,7 @@ public void Video_ToMP4_Resize()
[TestMethod] [TestMethod]
public void Video_ToMP4_Resize_Args() 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] [DataTestMethod]
@ -547,7 +545,6 @@ public void Video_With_Only_Audio_Should_Extract_Metadata()
Assert.AreEqual(null, video.PrimaryVideoStream); Assert.AreEqual(null, video.PrimaryVideoStream);
Assert.AreEqual("aac", video.PrimaryAudioStream.CodecName); Assert.AreEqual("aac", video.PrimaryAudioStream.CodecName);
Assert.AreEqual(79.5, video.Duration.TotalSeconds, 0.5); Assert.AreEqual(79.5, video.Duration.TotalSeconds, 0.5);
// Assert.AreEqual(1.25, video.Size);
} }
[TestMethod] [TestMethod]
@ -621,12 +618,13 @@ public void Video_TranscodeInMemory()
var reader = new StreamPipeDataReader(resStream); var reader = new StreamPipeDataReader(resStream);
var writer = new RawVideoPipeDataWriter(BitmapSource.CreateBitmaps(128, System.Drawing.Imaging.PixelFormat.Format24bppRgb, 128, 128)); var writer = new RawVideoPipeDataWriter(BitmapSource.CreateBitmaps(128, System.Drawing.Imaging.PixelFormat.Format24bppRgb, 128, 128));
FFMpegArguments var result = FFMpegArguments
.FromPipe(writer) .FromPipe(writer)
.WithVideoCodec("vp9") .WithVideoCodec("vp9")
.ForceFormat("webm") .ForceFormat("webm")
.OutputToPipe(reader) .OutputToPipe(reader)
.ProcessSynchronously(); .ProcessSynchronously();
Assert.IsTrue(result);
resStream.Position = 0; resStream.Position = 0;
var vi = FFProbe.Analyse(resStream); var vi = FFProbe.Analyse(resStream);

View file

@ -1,5 +1,5 @@
using FFMpegCore.Enums; using FFMpegCore.Enums;
using FFMpegCore.Exceptions; using FFMpegCore.Models;
namespace FFMpegCore.Arguments namespace FFMpegCore.Arguments
{ {

View file

@ -1,5 +1,5 @@
using FFMpegCore.Enums; using FFMpegCore.Enums;
using FFMpegCore.Exceptions; using FFMpegCore.Models;
namespace FFMpegCore.Arguments namespace FFMpegCore.Arguments
{ {

View file

@ -1,4 +1,4 @@
using FFMpegCore.Enums; using FFMpegCore.Models;
namespace FFMpegCore.Arguments namespace FFMpegCore.Arguments
{ {

View file

@ -1,4 +1,4 @@
using FFMpegCore.Enums; using FFMpegCore.Models;
namespace FFMpegCore.Arguments namespace FFMpegCore.Arguments
{ {

View file

@ -50,7 +50,7 @@ public interface IArgument
public interface IInputOutputArgument : IArgument public interface IInputOutputArgument : IArgument
{ {
void Pre() {} void Pre() {}
Task During(CancellationToken? cancellationToken = null) => Task.CompletedTask; Task During(CancellationToken cancellationToken) => Task.CompletedTask;
void Post() {} void Post() {}
} }

View file

@ -1,6 +1,6 @@
using System; using System;
using System.IO; using System.IO;
using FFMpegCore.Exceptions; using FFMpegCore.Models;
namespace FFMpegCore.Arguments namespace FFMpegCore.Arguments
{ {
@ -11,11 +11,13 @@ public class OutputArgument : IOutputArgument
{ {
public readonly string Path; public readonly string Path;
public readonly bool Overwrite; 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; Path = path;
Overwrite = overwrite; Overwrite = overwrite;
VerifyOutputExists = verifyOutputExists;
} }
public void Pre() public void Pre()
@ -25,7 +27,7 @@ public void Pre()
} }
public void Post() public void Post()
{ {
if (!File.Exists(Path)) if (VerifyOutputExists && !File.Exists(Path))
throw new FFMpegException(FFMpegExceptionType.File, "Output file was not created"); throw new FFMpegException(FFMpegExceptionType.File, "Output file was not created");
} }

View file

@ -34,16 +34,15 @@ public void Post()
Pipe = null!; Pipe = null!;
} }
public async Task During(CancellationToken? cancellationToken = null) public async Task During(CancellationToken cancellationToken)
{ {
try try
{ {
await ProcessDataAsync(cancellationToken ?? CancellationToken.None).ConfigureAwait(false); await ProcessDataAsync(cancellationToken).ConfigureAwait(false);
} }
catch (TaskCanceledException) catch (TaskCanceledException)
{ {
} }
Post();
} }
public abstract Task ProcessDataAsync(CancellationToken token); public abstract Task ProcessDataAsync(CancellationToken token);

View file

@ -1,5 +1,5 @@
using FFMpegCore.Enums; using FFMpegCore.Enums;
using FFMpegCore.Exceptions; using FFMpegCore.Models;
namespace FFMpegCore.Arguments namespace FFMpegCore.Arguments
{ {

View file

@ -0,0 +1,9 @@
namespace FFMpegCore.Enums
{
public enum Channel
{
Audio,
Video,
Both
}
}

View 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,
}
}

View file

@ -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
}
}

View file

@ -0,0 +1,9 @@
namespace FFMpegCore.Enums
{
public enum FeatureStatus
{
Unknown,
NotSupported,
Supported,
}
}

View file

@ -0,0 +1,8 @@
namespace FFMpegCore.Enums
{
public enum Filter
{
H264_Mp4ToAnnexB,
Aac_AdtstoAsc
}
}

View file

@ -5,12 +5,13 @@
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using FFMpegCore.Enums; using FFMpegCore.Enums;
using FFMpegCore.Exceptions;
using FFMpegCore.Helpers; using FFMpegCore.Helpers;
using FFMpegCore.Models;
using FFMpegCore.Utils;
namespace FFMpegCore namespace FFMpegCore
{ {
public static class FFMpeg public static partial class FFMpeg
{ {
/// <summary> /// <summary>
/// Saves a 'png' thumbnail from the input video. /// Saves a 'png' thumbnail from the input video.
@ -324,178 +325,7 @@ public static bool ReplaceAudio(string input, string inputAudio, string output,
.ProcessSynchronously(); .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) private static void Cleanup(IEnumerable<string> pathList)
{ {

View file

@ -4,8 +4,8 @@
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using FFMpegCore.Exceptions;
using FFMpegCore.Helpers; using FFMpegCore.Helpers;
using FFMpegCore.Models;
using Instances; using Instances;
namespace FFMpegCore namespace FFMpegCore

View file

@ -7,6 +7,7 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using FFMpegCore.Arguments; using FFMpegCore.Arguments;
using FFMpegCore.Enums; using FFMpegCore.Enums;
using FFMpegCore.Models;
using FFMpegCore.Pipes; using FFMpegCore.Pipes;
namespace FFMpegCore namespace FFMpegCore
@ -85,8 +86,8 @@ private FFMpegArguments(IInputArgument inputArgument)
public FFMpegArguments DrawText(DrawTextOptions drawTextOptions) => WithArgument(new DrawTextArgument(drawTextOptions)); 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(string file, bool overwrite = false, bool verifyOutputExists = true) => ToProcessor(new OutputArgument(file, overwrite, verifyOutputExists));
public FFMpegArgumentProcessor OutputToFile(Uri uri, bool overwrite = false) => ToProcessor(new OutputArgument(uri.AbsolutePath, overwrite)); 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 FFMpegArgumentProcessor OutputToPipe(IPipeDataReader reader) => ToProcessor(new OutputPipeArgument(reader));
public FFMpegArguments WithArgument(IArgument argument) public FFMpegArguments WithArgument(IArgument argument)
@ -106,7 +107,7 @@ internal void Pre()
_inputArgument.Pre(); _inputArgument.Pre();
_outputArgument.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); await Task.WhenAll(_inputArgument.During(cancellationToken), _outputArgument.During(cancellationToken)).ConfigureAwait(false);
} }

View file

@ -1,6 +1,6 @@
using FFMpegCore.Enums; using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using FFMpegCore.Models;
namespace FFMpegCore namespace FFMpegCore
{ {
@ -18,7 +18,7 @@ public static IReadOnlyDictionary<string, PixelFormat> PixelFormats
if (_pixelFormats == null) //First check not thread safe if (_pixelFormats == null) //First check not thread safe
lock (_syncObject) lock (_syncObject)
if (_pixelFormats == null)//Second check thread safe if (_pixelFormats == null)//Second check thread safe
_pixelFormats = FFMpeg.GetPixelFormatsInternal().ToDictionary(x => x.Name); _pixelFormats = FFMpegUtils.GetPixelFormatsInternal().ToDictionary(x => x.Name);
return _pixelFormats; return _pixelFormats;
} }
@ -31,7 +31,7 @@ public static IReadOnlyDictionary<string, Codec> Codecs
if (_codecs == null) //First check not thread safe if (_codecs == null) //First check not thread safe
lock (_syncObject) lock (_syncObject)
if (_codecs == null)//Second check thread safe if (_codecs == null)//Second check thread safe
_codecs = FFMpeg.GetCodecsInternal(); _codecs = FFMpegUtils.GetCodecsInternal();
return _codecs; return _codecs;
} }
@ -44,7 +44,7 @@ public static IReadOnlyDictionary<string, ContainerFormat> ContainerFormats
if (_containers == null) //First check not thread safe if (_containers == null) //First check not thread safe
lock (_syncObject) lock (_syncObject)
if (_containers == null)//Second check thread safe if (_containers == null)//Second check thread safe
_containers = FFMpeg.GetContainersFormatsInternal().ToDictionary(x => x.Name); _containers = FFMpegUtils.GetContainersFormatsInternal().ToDictionary(x => x.Name);
return _containers; return _containers;
} }

View 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
}
}

View file

@ -1,22 +1,13 @@
using FFMpegCore.Exceptions; using System;
using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using FFMpegCore.Enums;
namespace FFMpegCore.Enums namespace FFMpegCore.Models
{ {
public enum FeatureStatus
{
Unknown,
NotSupported,
Supported,
}
public class Codec 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 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+(.+)"); private static readonly Regex DecodersEncodersFormatRegex = new Regex(@"([VASD\.])([F\.])([S\.])([X\.])([B\.])([D\.])\s+([a-z0-9_-]+)\s+(.+)", RegexOptions.Compiled);
public class FeatureLevel public class FeatureLevel
{ {
@ -58,7 +49,7 @@ internal Codec(string name, CodecType type)
internal static bool TryParseFromCodecs(string line, out Codec codec) internal static bool TryParseFromCodecs(string line, out Codec codec)
{ {
var match = _codecsFormatRegex.Match(line); var match = CodecsFormatRegex.Match(line);
if (!match.Success) if (!match.Success)
{ {
codec = null!; codec = null!;
@ -81,20 +72,21 @@ internal static bool TryParseFromCodecs(string line, out Codec codec)
return false; return false;
} }
codec = new Codec(name, type); codec = new Codec(name, type)
{
codec.DecodingSupported = match.Groups[1].Value != "."; DecodingSupported = match.Groups[1].Value != ".",
codec.EncodingSupported = match.Groups[2].Value != "."; EncodingSupported = match.Groups[2].Value != ".",
codec.IsIntraFrameOnly = match.Groups[4].Value != "."; IsIntraFrameOnly = match.Groups[4].Value != ".",
codec.IsLossy = match.Groups[5].Value != "."; IsLossy = match.Groups[5].Value != ".",
codec.IsLossless = match.Groups[6].Value != "."; IsLossless = match.Groups[6].Value != ".",
codec.Description = match.Groups[8].Value; Description = match.Groups[8].Value
};
return true; return true;
} }
internal static bool TryParseFromEncodersDecoders(string line, out Codec codec, bool isEncoder) internal static bool TryParseFromEncodersDecoders(string line, out Codec codec, bool isEncoder)
{ {
var match = _decodersEncodersFormatRegex.Match(line); var match = DecodersEncodersFormatRegex.Match(line);
if (!match.Success) if (!match.Success)
{ {
codec = null!; codec = null!;
@ -135,7 +127,7 @@ internal static bool TryParseFromEncodersDecoders(string line, out Codec codec,
internal void Merge(Codec other) internal void Merge(Codec other)
{ {
if (Name != other.Name) 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; Type |= other.Type;
DecodingSupported |= other.DecodingSupported; DecodingSupported |= other.DecodingSupported;

View file

@ -1,9 +1,6 @@
using System; using System.Text.RegularExpressions;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
namespace FFMpegCore.Enums namespace FFMpegCore.Models
{ {
public class ContainerFormat public class ContainerFormat
{ {

View file

@ -1,6 +1,6 @@
using System; using System;
namespace FFMpegCore.Exceptions namespace FFMpegCore.Models
{ {
public enum FFMpegExceptionType public enum FFMpegExceptionType
{ {

View file

@ -1,13 +1,10 @@
using System; using System.Text.RegularExpressions;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
namespace FFMpegCore.Enums namespace FFMpegCore.Models
{ {
public class PixelFormat 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 InputConversionSupported { get; private set; }
public bool OutputConversionSupported { 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) internal static bool TryParse(string line, out PixelFormat fmt)
{ {
var match = _formatRegex.Match(line); var match = FormatRegex.Match(line);
if (!match.Success) if (!match.Success)
{ {
fmt = null!; fmt = null!;

View file

@ -2,7 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using FFMpegCore.Exceptions; using FFMpegCore.Models;
namespace FFMpegCore.Pipes namespace FFMpegCore.Pipes
{ {

View 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");
}
}

View file

@ -1,6 +1,7 @@
using System; using System;
using FFMpegCore.Models;
namespace FFMpegCore.Enums namespace FFMpegCore.Utils
{ {
public static class FileExtension public static class FileExtension
{ {

View 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");
}
}

View 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");
}
}

View file

@ -32,8 +32,4 @@
<PackageReference Include="System.Text.Json" Version="4.7.1" /> <PackageReference Include="System.Text.Json" Version="4.7.1" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Folder Include="FFMpeg\Models\" />
</ItemGroup>
</Project> </Project>

View file

@ -1,10 +1,10 @@
using System; using System.IO;
using System.IO;
using System.Text.Json; using System.Text.Json;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using FFMpegCore.Arguments; using FFMpegCore.Arguments;
using FFMpegCore.Exceptions;
using FFMpegCore.Helpers; using FFMpegCore.Helpers;
using FFMpegCore.Models;
using FFMpegCore.Pipes; using FFMpegCore.Pipes;
using Instances; using Instances;
@ -28,7 +28,7 @@ public static MediaAnalysis Analyse(System.IO.Stream stream, int outputCapacity
var task = instance.FinishedRunning(); var task = instance.FinishedRunning();
try try
{ {
pipeArgument.During().ConfigureAwait(false).GetAwaiter().GetResult(); pipeArgument.During(CancellationToken.None).ConfigureAwait(false).GetAwaiter().GetResult();
} }
catch (IOException) { } catch (IOException) { }
finally finally
@ -57,9 +57,9 @@ public static async Task<MediaAnalysis> AnalyseAsync(System.IO.Stream stream, in
var task = instance.FinishedRunning(); var task = instance.FinishedRunning();
try try
{ {
await pipeArgument.During(); await pipeArgument.During(CancellationToken.None);
} }
catch(IOException) catch (IOException)
{ {
} }
finally finally

View file

@ -8,67 +8,4 @@ public class FFProbeAnalysis
[JsonPropertyName("streams")] [JsonPropertyName("streams")]
public List<Stream> Streams { get; set; } = null!; 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!;
}
} }

View file

@ -10,24 +10,29 @@ internal MediaAnalysis(string path, FFProbeAnalysis analysis)
{ {
VideoStreams = analysis.Streams.Where(stream => stream.CodecType == "video").Select(ParseVideoStream).ToList(); VideoStreams = analysis.Streams.Where(stream => stream.CodecType == "video").Select(ParseVideoStream).ToList();
AudioStreams = analysis.Streams.Where(stream => stream.CodecType == "audio").Select(ParseAudioStream).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(); PrimaryVideoStream = VideoStreams.OrderBy(stream => stream.Index).FirstOrDefault();
PrimaryAudioStream = AudioStreams.OrderBy(stream => stream.Index).FirstOrDefault(); PrimaryAudioStream = AudioStreams.OrderBy(stream => stream.Index).FirstOrDefault();
PrimaryTextStream = TextStreams.OrderBy(stream => stream.Index).FirstOrDefault();
Path = path; Path = path;
} }
public string Path { get; } public string Path { get; }
public string Extension => System.IO.Path.GetExtension(Path); public string Extension => System.IO.Path.GetExtension(Path);
public TimeSpan Duration => TimeSpan.FromSeconds(Math.Max( public TimeSpan Duration => TimeSpan.FromSeconds(Math.Max(
PrimaryVideoStream?.Duration.TotalSeconds ?? 0, PrimaryVideoStream?.Duration.TotalSeconds ?? 0,
PrimaryAudioStream?.Duration.TotalSeconds ?? 0)); PrimaryAudioStream?.Duration.TotalSeconds ?? 0));
public AudioStream PrimaryAudioStream { get; }
public VideoStream PrimaryVideoStream { get; } public VideoStream PrimaryVideoStream { get; }
public AudioStream PrimaryAudioStream { get; }
public TextStream PrimaryTextStream { get; }
public List<VideoStream> VideoStreams { get; } public List<VideoStream> VideoStreams { get; }
public List<AudioStream> AudioStreams { get; } public List<AudioStream> AudioStreams { get; }
public List<TextStream> TextStreams { get; set; }
private VideoStream ParseVideoStream(Stream stream) private VideoStream ParseVideoStream(Stream stream)
{ {
@ -45,7 +50,8 @@ private VideoStream ParseVideoStream(Stream stream)
Height = stream.Height!.Value, Height = stream.Height!.Value,
Width = stream.Width!.Value, Width = stream.Width!.Value,
Profile = stream.Profile, 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, Channels = stream.Channels ?? default,
ChannelLayout = stream.ChannelLayout, ChannelLayout = stream.ChannelLayout,
Duration = TimeSpan.FromSeconds(ParseDoubleInvariant(stream.Duration ?? stream.Tags.Duration ?? "0")), 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
}; };
} }

View 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);
}
}

View file

@ -1,16 +1,13 @@
using FFMpegCore.Enums; using System;
using System;
namespace FFMpegCore namespace FFMpegCore
{ {
public class MediaStream public abstract class SimpleStream
{ {
public int Index { get; internal set; } public int Index { get; internal set; }
public string CodecName { get; internal set; } = null!; public string CodecName { get; internal set; } = null!;
public string CodecLongName { get; internal set; } = null!; public string CodecLongName { get; internal set; } = null!;
public int BitRate { get; internal set; }
public TimeSpan Duration { get; internal set; } public TimeSpan Duration { get; internal set; }
public string? Language { get; internal set; }
public Codec GetCodecInfo() => FFMpeg.GetCodec(CodecName);
} }
} }

View file

@ -0,0 +1,7 @@
namespace FFMpegCore
{
public class TextStream : SimpleStream
{
}
}

View file

@ -1,4 +1,4 @@
using FFMpegCore.Enums; using FFMpegCore.Models;
namespace FFMpegCore namespace FFMpegCore
{ {
@ -13,6 +13,6 @@ public class VideoStream : MediaStream
public double FrameRate { get; internal set; } public double FrameRate { get; internal set; }
public string PixelFormat { get; internal set; } = null!; public string PixelFormat { get; internal set; } = null!;
public PixelFormat GetPixelFormatInfo() => FFMpeg.GetPixelFormat(PixelFormat); public PixelFormat GetPixelFormatInfo() => FFMpegUtils.GetPixelFormat(PixelFormat);
} }
} }

View 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!;
}
}

View 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!;
}
}

View file

@ -1,7 +1,7 @@
using System; using System;
using System.Drawing; using System.Drawing;
using System.IO; using System.IO;
using FFMpegCore.Exceptions; using FFMpegCore.Models;
namespace FFMpegCore.Helpers namespace FFMpegCore.Helpers
{ {

View file

@ -1,4 +1,4 @@
using FFMpegCore.Exceptions; using FFMpegCore.Models;
namespace FFMpegCore.Helpers namespace FFMpegCore.Helpers
{ {

View file

@ -1,8 +1,8 @@
using System; using System;
using System.Drawing; using System.Drawing;
using System.IO; using System.IO;
using FFMpegCore.Enums;
using FFMpegCore.Helpers; using FFMpegCore.Helpers;
using FFMpegCore.Utils;
namespace FFMpegCore namespace FFMpegCore
{ {