mirror of
https://github.com/rosenbjerg/FFMpegCore.git
synced 2024-11-10 08:34:12 +01:00
Merge pull request #306 from rosenbjerg/bugfix/fixes
Bugfixes and improvements
Former-commit-id: aa6d4827fa
This commit is contained in:
commit
89c74e61ed
12 changed files with 109 additions and 87 deletions
10
.github/workflows/ci.yml
vendored
10
.github/workflows/ci.yml
vendored
|
@ -4,10 +4,18 @@ on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
|
paths:
|
||||||
|
- .github/workflows/ci.yml
|
||||||
|
- FFMpegCore/**
|
||||||
|
- FFMpegCore.Test/**
|
||||||
pull_request:
|
pull_request:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
- release
|
- release
|
||||||
|
paths:
|
||||||
|
- .github/workflows/ci.yml
|
||||||
|
- FFMpegCore/**
|
||||||
|
- FFMpegCore.Test/**
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
ci:
|
ci:
|
||||||
|
@ -22,7 +30,7 @@ jobs:
|
||||||
- name: Prepare .NET
|
- name: Prepare .NET
|
||||||
uses: actions/setup-dotnet@v1
|
uses: actions/setup-dotnet@v1
|
||||||
with:
|
with:
|
||||||
dotnet-version: '5.0.x'
|
dotnet-version: '6.0.x'
|
||||||
- name: Prepare FFMpeg
|
- name: Prepare FFMpeg
|
||||||
uses: FedericoCarboni/setup-ffmpeg@v1
|
uses: FedericoCarboni/setup-ffmpeg@v1
|
||||||
- name: Test with dotnet
|
- name: Test with dotnet
|
||||||
|
|
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
|
@ -12,7 +12,7 @@ jobs:
|
||||||
- name: Prepare .NET
|
- name: Prepare .NET
|
||||||
uses: actions/setup-dotnet@v1
|
uses: actions/setup-dotnet@v1
|
||||||
with:
|
with:
|
||||||
dotnet-version: '5.0.x'
|
dotnet-version: '6.0.x'
|
||||||
- name: Build solution
|
- name: Build solution
|
||||||
run: dotnet build --output build -c Release
|
run: dotnet build --output build -c Release
|
||||||
- name: Publish NuGet package
|
- name: Publish NuGet package
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net5.0</TargetFramework>
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net5.0</TargetFramework>
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
|
|
||||||
<IsPackable>false</IsPackable>
|
<IsPackable>false</IsPackable>
|
||||||
|
|
||||||
|
|
|
@ -247,7 +247,10 @@ public static bool Convert(
|
||||||
public static bool PosterWithAudio(string image, string audio, string output)
|
public static bool PosterWithAudio(string image, string audio, string output)
|
||||||
{
|
{
|
||||||
FFMpegHelper.ExtensionExceptionCheck(output, FileExtension.Mp4);
|
FFMpegHelper.ExtensionExceptionCheck(output, FileExtension.Mp4);
|
||||||
FFMpegHelper.ConversionSizeExceptionCheck(Image.FromFile(image));
|
using (var imageFile = Image.FromFile(image))
|
||||||
|
{
|
||||||
|
FFMpegHelper.ConversionSizeExceptionCheck(imageFile);
|
||||||
|
}
|
||||||
|
|
||||||
return FFMpegArguments
|
return FFMpegArguments
|
||||||
.FromFileInput(image, false, options => options
|
.FromFileInput(image, false, options => options
|
||||||
|
|
|
@ -2,9 +2,9 @@
|
||||||
{
|
{
|
||||||
public class AudioStream : MediaStream
|
public class AudioStream : MediaStream
|
||||||
{
|
{
|
||||||
public int Channels { get; internal set; }
|
public int Channels { get; set; }
|
||||||
public string ChannelLayout { get; internal set; } = null!;
|
public string ChannelLayout { get; set; } = null!;
|
||||||
public int SampleRateHz { get; internal set; }
|
public int SampleRateHz { get; set; }
|
||||||
public string Profile { get; internal set; } = null!;
|
public string Profile { get; set; } = null!;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -2,6 +2,7 @@
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
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.Exceptions;
|
||||||
|
@ -15,38 +16,33 @@ public static class FFProbe
|
||||||
{
|
{
|
||||||
public static IMediaAnalysis Analyse(string filePath, FFOptions? ffOptions = null)
|
public static IMediaAnalysis Analyse(string filePath, FFOptions? ffOptions = null)
|
||||||
{
|
{
|
||||||
if (!File.Exists(filePath))
|
ThrowIfInputFileDoesNotExist(filePath);
|
||||||
throw new FFMpegException(FFMpegExceptionType.File, $"No file found at '{filePath}'");
|
|
||||||
|
|
||||||
var processArguments = PrepareStreamAnalysisInstance(filePath, ffOptions ?? GlobalFFOptions.Current);
|
var processArguments = PrepareStreamAnalysisInstance(filePath, ffOptions ?? GlobalFFOptions.Current);
|
||||||
var result = processArguments.StartAndWaitForExit();
|
var result = processArguments.StartAndWaitForExit();
|
||||||
if (result.ExitCode != 0)
|
ThrowIfExitCodeNotZero(result);
|
||||||
throw new FFMpegException(FFMpegExceptionType.Process, $"ffprobe exited with non-zero exit-code ({result.ExitCode} - {string.Join("\n", result.ErrorData)})", null, string.Join("\n", result.ErrorData));
|
|
||||||
|
|
||||||
return ParseOutput(result);
|
return ParseOutput(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static FFProbeFrames GetFrames(string filePath, FFOptions? ffOptions = null)
|
public static FFProbeFrames GetFrames(string filePath, FFOptions? ffOptions = null)
|
||||||
{
|
{
|
||||||
if (!File.Exists(filePath))
|
ThrowIfInputFileDoesNotExist(filePath);
|
||||||
throw new FFMpegException(FFMpegExceptionType.File, $"No file found at '{filePath}'");
|
|
||||||
|
|
||||||
var instance = PrepareFrameAnalysisInstance(filePath, ffOptions ?? GlobalFFOptions.Current);
|
var instance = PrepareFrameAnalysisInstance(filePath, ffOptions ?? GlobalFFOptions.Current);
|
||||||
var result = instance.StartAndWaitForExit();
|
var result = instance.StartAndWaitForExit();
|
||||||
if (result.ExitCode != 0)
|
ThrowIfExitCodeNotZero(result);
|
||||||
throw new FFMpegException(FFMpegExceptionType.Process, $"ffprobe exited with non-zero exit-code ({result.ExitCode} - {string.Join("\n", result.ErrorData)})", null, string.Join("\n", result.ErrorData));
|
|
||||||
|
|
||||||
return ParseFramesOutput(result);
|
return ParseFramesOutput(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static FFProbePackets GetPackets(string filePath, FFOptions? ffOptions = null)
|
public static FFProbePackets GetPackets(string filePath, FFOptions? ffOptions = null)
|
||||||
{
|
{
|
||||||
if (!File.Exists(filePath))
|
ThrowIfInputFileDoesNotExist(filePath);
|
||||||
throw new FFMpegException(FFMpegExceptionType.File, $"No file found at '{filePath}'");
|
|
||||||
|
|
||||||
var instance = PreparePacketAnalysisInstance(filePath, ffOptions ?? GlobalFFOptions.Current);
|
var instance = PreparePacketAnalysisInstance(filePath, ffOptions ?? GlobalFFOptions.Current);
|
||||||
var result = instance.StartAndWaitForExit();
|
var result = instance.StartAndWaitForExit();
|
||||||
if (result.ExitCode != 0)
|
ThrowIfExitCodeNotZero(result);
|
||||||
throw new FFMpegException(FFMpegExceptionType.Process, $"ffprobe exited with non-zero exit-code ({result.ExitCode} - {string.Join("\n", result.ErrorData)})", null, string.Join("\n", result.ErrorData));
|
|
||||||
|
|
||||||
return ParsePacketsOutput(result);
|
return ParsePacketsOutput(result);
|
||||||
}
|
}
|
||||||
|
@ -55,8 +51,7 @@ public static IMediaAnalysis Analyse(Uri uri, FFOptions? ffOptions = null)
|
||||||
{
|
{
|
||||||
var instance = PrepareStreamAnalysisInstance(uri.AbsoluteUri, ffOptions ?? GlobalFFOptions.Current);
|
var instance = PrepareStreamAnalysisInstance(uri.AbsoluteUri, ffOptions ?? GlobalFFOptions.Current);
|
||||||
var result = instance.StartAndWaitForExit();
|
var result = instance.StartAndWaitForExit();
|
||||||
if (result.ExitCode != 0)
|
ThrowIfExitCodeNotZero(result);
|
||||||
throw new FFMpegException(FFMpegExceptionType.Process, $"ffprobe exited with non-zero exit-code ({result.ExitCode} - {string.Join("\n", result.ErrorData)})", null, string.Join("\n", result.ErrorData));
|
|
||||||
|
|
||||||
return ParseOutput(result);
|
return ParseOutput(result);
|
||||||
}
|
}
|
||||||
|
@ -78,64 +73,59 @@ public static IMediaAnalysis Analyse(Stream stream, FFOptions? ffOptions = null)
|
||||||
pipeArgument.Post();
|
pipeArgument.Post();
|
||||||
}
|
}
|
||||||
var result = task.ConfigureAwait(false).GetAwaiter().GetResult();
|
var result = task.ConfigureAwait(false).GetAwaiter().GetResult();
|
||||||
if (result.ExitCode != 0)
|
ThrowIfExitCodeNotZero(result);
|
||||||
throw new FFMpegException(FFMpegExceptionType.Process, $"ffprobe exited with non-zero exit-code ({result.ExitCode} - {string.Join("\n", result.ErrorData)})", null, string.Join("\n", result.ErrorData));
|
|
||||||
|
|
||||||
return ParseOutput(result);
|
return ParseOutput(result);
|
||||||
}
|
}
|
||||||
public static async Task<IMediaAnalysis> AnalyseAsync(string filePath, FFOptions? ffOptions = null)
|
|
||||||
|
public static async Task<IMediaAnalysis> AnalyseAsync(string filePath, FFOptions? ffOptions = null, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
if (!File.Exists(filePath))
|
ThrowIfInputFileDoesNotExist(filePath);
|
||||||
throw new FFMpegException(FFMpegExceptionType.File, $"No file found at '{filePath}'");
|
|
||||||
|
|
||||||
var instance = PrepareStreamAnalysisInstance(filePath, ffOptions ?? GlobalFFOptions.Current);
|
var instance = PrepareStreamAnalysisInstance(filePath, ffOptions ?? GlobalFFOptions.Current);
|
||||||
var result = await instance.StartAndWaitForExitAsync().ConfigureAwait(false);
|
var result = await instance.StartAndWaitForExitAsync(cancellationToken).ConfigureAwait(false);
|
||||||
if (result.ExitCode != 0)
|
ThrowIfExitCodeNotZero(result);
|
||||||
throw new FFMpegException(FFMpegExceptionType.Process, $"ffprobe exited with non-zero exit-code ({result.ExitCode} - {string.Join("\n", result.ErrorData)})", null, string.Join("\n", result.ErrorData));
|
|
||||||
|
|
||||||
return ParseOutput(result);
|
return ParseOutput(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<FFProbeFrames> GetFramesAsync(string filePath, FFOptions? ffOptions = null)
|
public static async Task<FFProbeFrames> GetFramesAsync(string filePath, FFOptions? ffOptions = null, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
if (!File.Exists(filePath))
|
ThrowIfInputFileDoesNotExist(filePath);
|
||||||
throw new FFMpegException(FFMpegExceptionType.File, $"No file found at '{filePath}'");
|
|
||||||
|
|
||||||
var instance = PrepareFrameAnalysisInstance(filePath, ffOptions ?? GlobalFFOptions.Current);
|
var instance = PrepareFrameAnalysisInstance(filePath, ffOptions ?? GlobalFFOptions.Current);
|
||||||
var result = await instance.StartAndWaitForExitAsync().ConfigureAwait(false);
|
var result = await instance.StartAndWaitForExitAsync(cancellationToken).ConfigureAwait(false);
|
||||||
return ParseFramesOutput(result);
|
return ParseFramesOutput(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<FFProbePackets> GetPacketsAsync(string filePath, FFOptions? ffOptions = null)
|
public static async Task<FFProbePackets> GetPacketsAsync(string filePath, FFOptions? ffOptions = null, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
if (!File.Exists(filePath))
|
ThrowIfInputFileDoesNotExist(filePath);
|
||||||
throw new FFMpegException(FFMpegExceptionType.File, $"No file found at '{filePath}'");
|
|
||||||
|
|
||||||
var instance = PreparePacketAnalysisInstance(filePath, ffOptions ?? GlobalFFOptions.Current);
|
var instance = PreparePacketAnalysisInstance(filePath, ffOptions ?? GlobalFFOptions.Current);
|
||||||
var result = await instance.StartAndWaitForExitAsync().ConfigureAwait(false);
|
var result = await instance.StartAndWaitForExitAsync(cancellationToken).ConfigureAwait(false);
|
||||||
return ParsePacketsOutput(result);
|
return ParsePacketsOutput(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<IMediaAnalysis> AnalyseAsync(Uri uri, FFOptions? ffOptions = null)
|
public static async Task<IMediaAnalysis> AnalyseAsync(Uri uri, FFOptions? ffOptions = null, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
var instance = PrepareStreamAnalysisInstance(uri.AbsoluteUri, ffOptions ?? GlobalFFOptions.Current);
|
var instance = PrepareStreamAnalysisInstance(uri.AbsoluteUri, ffOptions ?? GlobalFFOptions.Current);
|
||||||
var result = await instance.StartAndWaitForExitAsync().ConfigureAwait(false);
|
var result = await instance.StartAndWaitForExitAsync(cancellationToken).ConfigureAwait(false);
|
||||||
if (result.ExitCode != 0)
|
ThrowIfExitCodeNotZero(result);
|
||||||
throw new FFMpegException(FFMpegExceptionType.Process, $"ffprobe exited with non-zero exit-code ({result.ExitCode} - {string.Join("\n", result.ErrorData)})", null, string.Join("\n", result.ErrorData));
|
|
||||||
|
|
||||||
return ParseOutput(result);
|
return ParseOutput(result);
|
||||||
}
|
}
|
||||||
public static async Task<IMediaAnalysis> AnalyseAsync(Stream stream, FFOptions? ffOptions = null)
|
public static async Task<IMediaAnalysis> AnalyseAsync(Stream stream, FFOptions? ffOptions = null, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
var streamPipeSource = new StreamPipeSource(stream);
|
var streamPipeSource = new StreamPipeSource(stream);
|
||||||
var pipeArgument = new InputPipeArgument(streamPipeSource);
|
var pipeArgument = new InputPipeArgument(streamPipeSource);
|
||||||
var instance = PrepareStreamAnalysisInstance(pipeArgument.PipePath, ffOptions ?? GlobalFFOptions.Current);
|
var instance = PrepareStreamAnalysisInstance(pipeArgument.PipePath, ffOptions ?? GlobalFFOptions.Current);
|
||||||
pipeArgument.Pre();
|
pipeArgument.Pre();
|
||||||
|
|
||||||
var task = instance.StartAndWaitForExitAsync();
|
var task = instance.StartAndWaitForExitAsync(cancellationToken);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await pipeArgument.During().ConfigureAwait(false);
|
await pipeArgument.During(cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
catch(IOException)
|
catch(IOException)
|
||||||
{
|
{
|
||||||
|
@ -145,8 +135,7 @@ public static async Task<IMediaAnalysis> AnalyseAsync(Stream stream, FFOptions?
|
||||||
pipeArgument.Post();
|
pipeArgument.Post();
|
||||||
}
|
}
|
||||||
var result = await task.ConfigureAwait(false);
|
var result = await task.ConfigureAwait(false);
|
||||||
if (result.ExitCode != 0)
|
ThrowIfExitCodeNotZero(result);
|
||||||
throw new FFProbeProcessException($"ffprobe exited with non-zero exit-code ({result.ExitCode} - {string.Join("\n", result.ErrorData)})", result.ErrorData);
|
|
||||||
|
|
||||||
pipeArgument.Post();
|
pipeArgument.Post();
|
||||||
return ParseOutput(result);
|
return ParseOutput(result);
|
||||||
|
@ -174,7 +163,7 @@ private static FFProbeFrames ParseFramesOutput(IProcessResult instance)
|
||||||
NumberHandling = System.Text.Json.Serialization.JsonNumberHandling.AllowReadingFromString | System.Text.Json.Serialization.JsonNumberHandling.WriteAsString
|
NumberHandling = System.Text.Json.Serialization.JsonNumberHandling.AllowReadingFromString | System.Text.Json.Serialization.JsonNumberHandling.WriteAsString
|
||||||
}) ;
|
}) ;
|
||||||
|
|
||||||
return ffprobeAnalysis;
|
return ffprobeAnalysis!;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static FFProbePackets ParsePacketsOutput(IProcessResult instance)
|
private static FFProbePackets ParsePacketsOutput(IProcessResult instance)
|
||||||
|
@ -186,9 +175,25 @@ private static FFProbePackets ParsePacketsOutput(IProcessResult instance)
|
||||||
NumberHandling = System.Text.Json.Serialization.JsonNumberHandling.AllowReadingFromString | System.Text.Json.Serialization.JsonNumberHandling.WriteAsString
|
NumberHandling = System.Text.Json.Serialization.JsonNumberHandling.AllowReadingFromString | System.Text.Json.Serialization.JsonNumberHandling.WriteAsString
|
||||||
}) ;
|
}) ;
|
||||||
|
|
||||||
return ffprobeAnalysis;
|
return ffprobeAnalysis!;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void ThrowIfInputFileDoesNotExist(string filePath)
|
||||||
|
{
|
||||||
|
if (!File.Exists(filePath))
|
||||||
|
{
|
||||||
|
throw new FFMpegException(FFMpegExceptionType.File, $"No file found at '{filePath}'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ThrowIfExitCodeNotZero(IProcessResult result)
|
||||||
|
{
|
||||||
|
if (result.ExitCode != 0)
|
||||||
|
{
|
||||||
|
var message = $"ffprobe exited with non-zero exit-code ({result.ExitCode} - {string.Join("\n", result.ErrorData)})";
|
||||||
|
throw new FFMpegException(FFMpegExceptionType.Process, message, null, string.Join("\n", result.ErrorData));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static ProcessArguments PrepareStreamAnalysisInstance(string filePath, FFOptions ffOptions)
|
private static ProcessArguments PrepareStreamAnalysisInstance(string filePath, FFOptions ffOptions)
|
||||||
=> PrepareInstance($"-loglevel error -print_format json -show_format -sexagesimal -show_streams \"{filePath}\"", ffOptions);
|
=> PrepareInstance($"-loglevel error -print_format json -show_format -sexagesimal -show_streams \"{filePath}\"", ffOptions);
|
||||||
|
|
|
@ -6,7 +6,7 @@ namespace FFMpegCore
|
||||||
public class FFProbeFrameAnalysis
|
public class FFProbeFrameAnalysis
|
||||||
{
|
{
|
||||||
[JsonPropertyName("media_type")]
|
[JsonPropertyName("media_type")]
|
||||||
public string MediaType { get; set; }
|
public string MediaType { get; set; } = null!;
|
||||||
|
|
||||||
[JsonPropertyName("stream_index")]
|
[JsonPropertyName("stream_index")]
|
||||||
public int StreamIndex { get; set; }
|
public int StreamIndex { get; set; }
|
||||||
|
@ -18,25 +18,25 @@ public class FFProbeFrameAnalysis
|
||||||
public long PacketPts { get; set; }
|
public long PacketPts { get; set; }
|
||||||
|
|
||||||
[JsonPropertyName("pkt_pts_time")]
|
[JsonPropertyName("pkt_pts_time")]
|
||||||
public string PacketPtsTime { get; set; }
|
public string PacketPtsTime { get; set; } = null!;
|
||||||
|
|
||||||
[JsonPropertyName("pkt_dts")]
|
[JsonPropertyName("pkt_dts")]
|
||||||
public long PacketDts { get; set; }
|
public long PacketDts { get; set; }
|
||||||
|
|
||||||
[JsonPropertyName("pkt_dts_time")]
|
[JsonPropertyName("pkt_dts_time")]
|
||||||
public string PacketDtsTime { get; set; }
|
public string PacketDtsTime { get; set; } = null!;
|
||||||
|
|
||||||
[JsonPropertyName("best_effort_timestamp")]
|
[JsonPropertyName("best_effort_timestamp")]
|
||||||
public long BestEffortTimestamp { get; set; }
|
public long BestEffortTimestamp { get; set; }
|
||||||
|
|
||||||
[JsonPropertyName("best_effort_timestamp_time")]
|
[JsonPropertyName("best_effort_timestamp_time")]
|
||||||
public string BestEffortTimestampTime { get; set; }
|
public string BestEffortTimestampTime { get; set; } = null!;
|
||||||
|
|
||||||
[JsonPropertyName("pkt_duration")]
|
[JsonPropertyName("pkt_duration")]
|
||||||
public int PacketDuration { get; set; }
|
public int PacketDuration { get; set; }
|
||||||
|
|
||||||
[JsonPropertyName("pkt_duration_time")]
|
[JsonPropertyName("pkt_duration_time")]
|
||||||
public string PacketDurationTime { get; set; }
|
public string PacketDurationTime { get; set; } = null!;
|
||||||
|
|
||||||
[JsonPropertyName("pkt_pos")]
|
[JsonPropertyName("pkt_pos")]
|
||||||
public long PacketPos { get; set; }
|
public long PacketPos { get; set; }
|
||||||
|
@ -51,10 +51,10 @@ public class FFProbeFrameAnalysis
|
||||||
public long Height { get; set; }
|
public long Height { get; set; }
|
||||||
|
|
||||||
[JsonPropertyName("pix_fmt")]
|
[JsonPropertyName("pix_fmt")]
|
||||||
public string PixelFormat { get; set; }
|
public string PixelFormat { get; set; } = null!;
|
||||||
|
|
||||||
[JsonPropertyName("pict_type")]
|
[JsonPropertyName("pict_type")]
|
||||||
public string PictureType { get; set; }
|
public string PictureType { get; set; } = null!;
|
||||||
|
|
||||||
[JsonPropertyName("coded_picture_number")]
|
[JsonPropertyName("coded_picture_number")]
|
||||||
public long CodedPictureNumber { get; set; }
|
public long CodedPictureNumber { get; set; }
|
||||||
|
@ -72,12 +72,12 @@ public class FFProbeFrameAnalysis
|
||||||
public int RepeatPicture { get; set; }
|
public int RepeatPicture { get; set; }
|
||||||
|
|
||||||
[JsonPropertyName("chroma_location")]
|
[JsonPropertyName("chroma_location")]
|
||||||
public string ChromaLocation { get; set; }
|
public string ChromaLocation { get; set; } = null!;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class FFProbeFrames
|
public class FFProbeFrames
|
||||||
{
|
{
|
||||||
[JsonPropertyName("frames")]
|
[JsonPropertyName("frames")]
|
||||||
public List<FFProbeFrameAnalysis> Frames { get; set; }
|
public List<FFProbeFrameAnalysis> Frames { get; set; } = null!;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@ private MediaFormat ParseFormat(Format analysisFormat)
|
||||||
StreamCount = analysisFormat.NbStreams,
|
StreamCount = analysisFormat.NbStreams,
|
||||||
ProbeScore = analysisFormat.ProbeScore,
|
ProbeScore = analysisFormat.ProbeScore,
|
||||||
BitRate = long.Parse(analysisFormat.BitRate ?? "0"),
|
BitRate = long.Parse(analysisFormat.BitRate ?? "0"),
|
||||||
Tags = analysisFormat.Tags,
|
Tags = analysisFormat.Tags.ToCaseInsensitive(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,7 +68,7 @@ private VideoStream ParseVideoStream(FFProbeStream stream)
|
||||||
Rotation = (int)float.Parse(stream.GetRotate() ?? "0"),
|
Rotation = (int)float.Parse(stream.GetRotate() ?? "0"),
|
||||||
Language = stream.GetLanguage(),
|
Language = stream.GetLanguage(),
|
||||||
Disposition = MediaAnalysisUtils.FormatDisposition(stream.Disposition),
|
Disposition = MediaAnalysisUtils.FormatDisposition(stream.Disposition),
|
||||||
Tags = stream.Tags,
|
Tags = stream.Tags.ToCaseInsensitive(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,7 +89,7 @@ private AudioStream ParseAudioStream(FFProbeStream stream)
|
||||||
Profile = stream.Profile,
|
Profile = stream.Profile,
|
||||||
Language = stream.GetLanguage(),
|
Language = stream.GetLanguage(),
|
||||||
Disposition = MediaAnalysisUtils.FormatDisposition(stream.Disposition),
|
Disposition = MediaAnalysisUtils.FormatDisposition(stream.Disposition),
|
||||||
Tags = stream.Tags,
|
Tags = stream.Tags.ToCaseInsensitive(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,15 +104,20 @@ private SubtitleStream ParseSubtitleStream(FFProbeStream stream)
|
||||||
Duration = MediaAnalysisUtils.ParseDuration(stream),
|
Duration = MediaAnalysisUtils.ParseDuration(stream),
|
||||||
Language = stream.GetLanguage(),
|
Language = stream.GetLanguage(),
|
||||||
Disposition = MediaAnalysisUtils.FormatDisposition(stream.Disposition),
|
Disposition = MediaAnalysisUtils.FormatDisposition(stream.Disposition),
|
||||||
Tags = stream.Tags,
|
Tags = stream.Tags.ToCaseInsensitive(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class MediaAnalysisUtils
|
public static class MediaAnalysisUtils
|
||||||
{
|
{
|
||||||
private static readonly Regex DurationRegex = new Regex(@"^(\d+):(\d{1,2}):(\d{1,2})\.(\d{1,3})", RegexOptions.Compiled);
|
private static readonly Regex DurationRegex = new Regex(@"^(\d+):(\d{1,2}):(\d{1,2})\.(\d{1,3})", RegexOptions.Compiled);
|
||||||
|
|
||||||
|
internal static Dictionary<string, string>? ToCaseInsensitive(this Dictionary<string, string>? dictionary)
|
||||||
|
{
|
||||||
|
return dictionary?.ToDictionary(tag => tag.Key, tag => tag.Value, StringComparer.OrdinalIgnoreCase) ?? new Dictionary<string, string>();
|
||||||
|
}
|
||||||
public static double DivideRatio((double, double) ratio) => ratio.Item1 / ratio.Item2;
|
public static double DivideRatio((double, double) ratio) => ratio.Item1 / ratio.Item2;
|
||||||
|
|
||||||
public static (int, int) ParseRatioInt(string input, char separator)
|
public static (int, int) ParseRatioInt(string input, char separator)
|
||||||
|
@ -183,7 +188,7 @@ public static TimeSpan ParseDuration(FFProbeStream ffProbeStream)
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var result = new Dictionary<string, bool>(disposition.Count);
|
var result = new Dictionary<string, bool>(disposition.Count, StringComparer.Ordinal);
|
||||||
|
|
||||||
foreach (var pair in disposition)
|
foreach (var pair in disposition)
|
||||||
{
|
{
|
||||||
|
|
|
@ -5,18 +5,18 @@
|
||||||
|
|
||||||
namespace FFMpegCore
|
namespace FFMpegCore
|
||||||
{
|
{
|
||||||
public class MediaStream
|
public abstract class MediaStream
|
||||||
{
|
{
|
||||||
public int Index { get; internal set; }
|
public int Index { get; set; }
|
||||||
public string CodecName { get; internal set; } = null!;
|
public string CodecName { get; set; } = null!;
|
||||||
public string CodecLongName { get; internal set; } = null!;
|
public string CodecLongName { get; set; } = null!;
|
||||||
public string CodecTagString { get; set; } = null!;
|
public string CodecTagString { get; set; } = null!;
|
||||||
public string CodecTag { get; set; } = null!;
|
public string CodecTag { get; set; } = null!;
|
||||||
public long BitRate { get; internal set; }
|
public long BitRate { get; set; }
|
||||||
public TimeSpan Duration { get; internal set; }
|
public TimeSpan Duration { get; set; }
|
||||||
public string? Language { get; internal set; }
|
public string? Language { get; set; }
|
||||||
public Dictionary<string, bool>? Disposition { get; internal set; }
|
public Dictionary<string, bool>? Disposition { get; set; }
|
||||||
public Dictionary<string, string>? Tags { get; internal set; }
|
public Dictionary<string, string>? Tags { get; set; }
|
||||||
|
|
||||||
public Codec GetCodecInfo() => FFMpeg.GetCodec(CodecName);
|
public Codec GetCodecInfo() => FFMpeg.GetCodec(CodecName);
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ namespace FFMpegCore
|
||||||
public class FFProbePacketAnalysis
|
public class FFProbePacketAnalysis
|
||||||
{
|
{
|
||||||
[JsonPropertyName("codec_type")]
|
[JsonPropertyName("codec_type")]
|
||||||
public string CodecType { get; set; }
|
public string CodecType { get; set; } = null!;
|
||||||
|
|
||||||
[JsonPropertyName("stream_index")]
|
[JsonPropertyName("stream_index")]
|
||||||
public int StreamIndex { get; set; }
|
public int StreamIndex { get; set; }
|
||||||
|
@ -15,19 +15,19 @@ public class FFProbePacketAnalysis
|
||||||
public long Pts { get; set; }
|
public long Pts { get; set; }
|
||||||
|
|
||||||
[JsonPropertyName("pts_time")]
|
[JsonPropertyName("pts_time")]
|
||||||
public string PtsTime { get; set; }
|
public string PtsTime { get; set; } = null!;
|
||||||
|
|
||||||
[JsonPropertyName("dts")]
|
[JsonPropertyName("dts")]
|
||||||
public long Dts { get; set; }
|
public long Dts { get; set; }
|
||||||
|
|
||||||
[JsonPropertyName("dts_time")]
|
[JsonPropertyName("dts_time")]
|
||||||
public string DtsTime { get; set; }
|
public string DtsTime { get; set; } = null!;
|
||||||
|
|
||||||
[JsonPropertyName("duration")]
|
[JsonPropertyName("duration")]
|
||||||
public int Duration { get; set; }
|
public int Duration { get; set; }
|
||||||
|
|
||||||
[JsonPropertyName("duration_time")]
|
[JsonPropertyName("duration_time")]
|
||||||
public string DurationTime { get; set; }
|
public string DurationTime { get; set; } = null!;
|
||||||
|
|
||||||
[JsonPropertyName("size")]
|
[JsonPropertyName("size")]
|
||||||
public int Size { get; set; }
|
public int Size { get; set; }
|
||||||
|
@ -36,12 +36,12 @@ public class FFProbePacketAnalysis
|
||||||
public long Pos { get; set; }
|
public long Pos { get; set; }
|
||||||
|
|
||||||
[JsonPropertyName("flags")]
|
[JsonPropertyName("flags")]
|
||||||
public string Flags { get; set; }
|
public string Flags { get; set; } = null!;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class FFProbePackets
|
public class FFProbePackets
|
||||||
{
|
{
|
||||||
[JsonPropertyName("packets")]
|
[JsonPropertyName("packets")]
|
||||||
public List<FFProbePacketAnalysis> Packets { get; set; }
|
public List<FFProbePacketAnalysis> Packets { get; set; } = null!;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,15 +4,16 @@ namespace FFMpegCore
|
||||||
{
|
{
|
||||||
public class VideoStream : MediaStream
|
public class VideoStream : MediaStream
|
||||||
{
|
{
|
||||||
public double AvgFrameRate { get; internal set; }
|
public double AvgFrameRate { get; set; }
|
||||||
public int BitsPerRawSample { get; internal set; }
|
public int BitsPerRawSample { get; set; }
|
||||||
public (int Width, int Height) DisplayAspectRatio { get; internal set; }
|
public (int Width, int Height) DisplayAspectRatio { get; set; }
|
||||||
public string Profile { get; internal set; } = null!;
|
public string Profile { get; set; } = null!;
|
||||||
public int Width { get; internal set; }
|
public int Width { get; set; }
|
||||||
public int Height { get; internal set; }
|
public int Height { get; set; }
|
||||||
public double FrameRate { get; internal set; }
|
public double FrameRate { get; set; }
|
||||||
public string PixelFormat { get; internal set; } = null!;
|
public string PixelFormat { get; set; } = null!;
|
||||||
public int Rotation { get; set; }
|
public int Rotation { get; set; }
|
||||||
|
public double AverageFrameRate { get; set; }
|
||||||
|
|
||||||
public PixelFormat GetPixelFormatInfo() => FFMpeg.GetPixelFormat(PixelFormat);
|
public PixelFormat GetPixelFormatInfo() => FFMpeg.GetPixelFormat(PixelFormat);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue