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 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
|
||||||
{
|
{
|
||||||
|
|
|
@ -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
|
||||||
{
|
{
|
||||||
|
|
|
@ -49,7 +49,7 @@ public async Task Probe_Async_Success()
|
||||||
var info = await FFProbe.AnalyseAsync(VideoLibrary.LocalVideo.FullName);
|
var info = await FFProbe.AnalyseAsync(VideoLibrary.LocalVideo.FullName);
|
||||||
Assert.AreEqual(13, info.Duration.Seconds);
|
Assert.AreEqual(13, info.Duration.Seconds);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod, Timeout(10000)]
|
[TestMethod, Timeout(10000)]
|
||||||
public void Probe_Success_FromStream()
|
public void Probe_Success_FromStream()
|
||||||
{
|
{
|
||||||
|
|
|
@ -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"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
{
|
{
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
@ -206,13 +208,8 @@ public void Convert(ContainerFormat type, Action<MediaAnalysis> validationMethod
|
||||||
File.Delete(output);
|
File.Delete(output);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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,13 +618,14 @@ 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);
|
||||||
Assert.AreEqual(vi.PrimaryVideoStream.Width, 128);
|
Assert.AreEqual(vi.PrimaryVideoStream.Width, 128);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
using FFMpegCore.Enums;
|
using FFMpegCore.Enums;
|
||||||
using FFMpegCore.Exceptions;
|
using FFMpegCore.Models;
|
||||||
|
|
||||||
namespace FFMpegCore.Arguments
|
namespace FFMpegCore.Arguments
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
using FFMpegCore.Enums;
|
using FFMpegCore.Enums;
|
||||||
using FFMpegCore.Exceptions;
|
using FFMpegCore.Models;
|
||||||
|
|
||||||
namespace FFMpegCore.Arguments
|
namespace FFMpegCore.Arguments
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
using FFMpegCore.Enums;
|
using FFMpegCore.Models;
|
||||||
|
|
||||||
namespace FFMpegCore.Arguments
|
namespace FFMpegCore.Arguments
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
using FFMpegCore.Enums;
|
using FFMpegCore.Models;
|
||||||
|
|
||||||
namespace FFMpegCore.Arguments
|
namespace FFMpegCore.Arguments
|
||||||
{
|
{
|
||||||
|
|
|
@ -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() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
using FFMpegCore.Enums;
|
using FFMpegCore.Enums;
|
||||||
using FFMpegCore.Exceptions;
|
using FFMpegCore.Models;
|
||||||
|
|
||||||
namespace FFMpegCore.Arguments
|
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.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)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
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;
|
|
||||||
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;
|
|
@ -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
|
||||||
{
|
{
|
|
@ -1,6 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace FFMpegCore.Exceptions
|
namespace FFMpegCore.Models
|
||||||
{
|
{
|
||||||
public enum FFMpegExceptionType
|
public enum FFMpegExceptionType
|
||||||
{
|
{
|
|
@ -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!;
|
|
@ -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
|
||||||
{
|
{
|
||||||
|
|
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 System;
|
||||||
|
using FFMpegCore.Models;
|
||||||
|
|
||||||
namespace FFMpegCore.Enums
|
namespace FFMpegCore.Utils
|
||||||
{
|
{
|
||||||
public static class FileExtension
|
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" />
|
<PackageReference Include="System.Text.Json" Version="4.7.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Folder Include="FFMpeg\Models\" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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!;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
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
|
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
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
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
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;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using FFMpegCore.Exceptions;
|
using FFMpegCore.Models;
|
||||||
|
|
||||||
namespace FFMpegCore.Helpers
|
namespace FFMpegCore.Helpers
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
using FFMpegCore.Exceptions;
|
using FFMpegCore.Models;
|
||||||
|
|
||||||
namespace FFMpegCore.Helpers
|
namespace FFMpegCore.Helpers
|
||||||
{
|
{
|
||||||
|
|
|
@ -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
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue