Merge pull request #167 from rosenbjerg/master

3.4.0
This commit is contained in:
Malte Rosenbjerg 2021-02-03 23:54:38 +01:00 committed by GitHub
commit d598d846a8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 33 additions and 18 deletions

View file

@ -10,6 +10,7 @@ public class ContainerFormat
public bool DemuxingSupported { get; private set; } public bool DemuxingSupported { get; private set; }
public bool MuxingSupported { get; private set; } public bool MuxingSupported { get; private set; }
public string Description { get; private set; } = null!; public string Description { get; private set; } = null!;
public string Extension public string Extension
{ {
get get
@ -36,8 +37,8 @@ internal static bool TryParse(string line, out ContainerFormat fmt)
fmt = new ContainerFormat(match.Groups[3].Value) fmt = new ContainerFormat(match.Groups[3].Value)
{ {
DemuxingSupported = match.Groups[1].Value == " ", DemuxingSupported = match.Groups[1].Value != " ",
MuxingSupported = match.Groups[2].Value == " ", MuxingSupported = match.Groups[2].Value != " ",
Description = match.Groups[4].Value Description = match.Groups[4].Value
}; };
return true; return true;

View file

@ -9,10 +9,10 @@
<Version>3.0.0.0</Version> <Version>3.0.0.0</Version>
<AssemblyVersion>3.0.0.0</AssemblyVersion> <AssemblyVersion>3.0.0.0</AssemblyVersion>
<FileVersion>3.0.0.0</FileVersion> <FileVersion>3.0.0.0</FileVersion>
<PackageReleaseNotes>- Support for changing encoding used for parsing ffmpeg/ffprobe output <PackageReleaseNotes>- return null from FFProbe.Analyse* when no media format was detected
- Support for registrering action to invoke on ffmpeg output</PackageReleaseNotes> - Expose tags as string dictionary on IMediaAnalysis (thanks hey-red)</PackageReleaseNotes>
<LangVersion>8</LangVersion> <LangVersion>8</LangVersion>
<PackageVersion>3.3.0</PackageVersion> <PackageVersion>3.4.0</PackageVersion>
<PackageLicenseExpression>MIT</PackageLicenseExpression> <PackageLicenseExpression>MIT</PackageLicenseExpression>
<Authors>Malte Rosenbjerg, Vlad Jerca</Authors> <Authors>Malte Rosenbjerg, Vlad Jerca</Authors>
<PackageTags>ffmpeg ffprobe convert video audio mediafile resize analyze muxing</PackageTags> <PackageTags>ffmpeg ffprobe convert video audio mediafile resize analyze muxing</PackageTags>
@ -31,7 +31,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Instances" Version="1.6.0" /> <PackageReference Include="Instances" Version="1.6.0" />
<PackageReference Include="System.Drawing.Common" Version="5.0.0" /> <PackageReference Include="System.Drawing.Common" Version="5.0.0" />
<PackageReference Include="System.Text.Json" Version="5.0.0" /> <PackageReference Include="System.Text.Json" Version="5.0.1" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View file

@ -12,7 +12,7 @@ namespace FFMpegCore
{ {
public static class FFProbe public static class FFProbe
{ {
public static IMediaAnalysis Analyse(string filePath, int outputCapacity = int.MaxValue) public static IMediaAnalysis? Analyse(string filePath, int outputCapacity = int.MaxValue)
{ {
if (!File.Exists(filePath)) if (!File.Exists(filePath))
throw new FFMpegException(FFMpegExceptionType.File, $"No file found at '{filePath}'"); throw new FFMpegException(FFMpegExceptionType.File, $"No file found at '{filePath}'");
@ -21,13 +21,13 @@ public static IMediaAnalysis Analyse(string filePath, int outputCapacity = int.M
instance.BlockUntilFinished(); instance.BlockUntilFinished();
return ParseOutput(filePath, instance); return ParseOutput(filePath, instance);
} }
public static IMediaAnalysis Analyse(Uri uri, int outputCapacity = int.MaxValue) public static IMediaAnalysis? Analyse(Uri uri, int outputCapacity = int.MaxValue)
{ {
using var instance = PrepareInstance(uri.AbsoluteUri, outputCapacity); using var instance = PrepareInstance(uri.AbsoluteUri, outputCapacity);
instance.BlockUntilFinished(); instance.BlockUntilFinished();
return ParseOutput(uri.AbsoluteUri, instance); return ParseOutput(uri.AbsoluteUri, instance);
} }
public static IMediaAnalysis Analyse(Stream stream, int outputCapacity = int.MaxValue) public static IMediaAnalysis? Analyse(Stream stream, int outputCapacity = int.MaxValue)
{ {
var streamPipeSource = new StreamPipeSource(stream); var streamPipeSource = new StreamPipeSource(stream);
var pipeArgument = new InputPipeArgument(streamPipeSource); var pipeArgument = new InputPipeArgument(streamPipeSource);
@ -50,7 +50,7 @@ public static IMediaAnalysis Analyse(Stream stream, int outputCapacity = int.Max
return ParseOutput(pipeArgument.PipePath, instance); return ParseOutput(pipeArgument.PipePath, instance);
} }
public static async Task<IMediaAnalysis> AnalyseAsync(string filePath, int outputCapacity = int.MaxValue) public static async Task<IMediaAnalysis?> AnalyseAsync(string filePath, int outputCapacity = int.MaxValue)
{ {
if (!File.Exists(filePath)) if (!File.Exists(filePath))
throw new FFMpegException(FFMpegExceptionType.File, $"No file found at '{filePath}'"); throw new FFMpegException(FFMpegExceptionType.File, $"No file found at '{filePath}'");
@ -59,13 +59,13 @@ public static async Task<IMediaAnalysis> AnalyseAsync(string filePath, int outpu
await instance.FinishedRunning(); await instance.FinishedRunning();
return ParseOutput(filePath, instance); return ParseOutput(filePath, instance);
} }
public static async Task<IMediaAnalysis> AnalyseAsync(Uri uri, int outputCapacity = int.MaxValue) public static async Task<IMediaAnalysis?> AnalyseAsync(Uri uri, int outputCapacity = int.MaxValue)
{ {
using var instance = PrepareInstance(uri.AbsoluteUri, outputCapacity); using var instance = PrepareInstance(uri.AbsoluteUri, outputCapacity);
await instance.FinishedRunning(); await instance.FinishedRunning();
return ParseOutput(uri.AbsoluteUri, instance); return ParseOutput(uri.AbsoluteUri, instance);
} }
public static async Task<IMediaAnalysis> AnalyseAsync(Stream stream, int outputCapacity = int.MaxValue) public static async Task<IMediaAnalysis?> AnalyseAsync(Stream stream, int outputCapacity = int.MaxValue)
{ {
var streamPipeSource = new StreamPipeSource(stream); var streamPipeSource = new StreamPipeSource(stream);
var pipeArgument = new InputPipeArgument(streamPipeSource); var pipeArgument = new InputPipeArgument(streamPipeSource);
@ -92,13 +92,14 @@ public static async Task<IMediaAnalysis> AnalyseAsync(Stream stream, int outputC
return ParseOutput(pipeArgument.PipePath, instance); return ParseOutput(pipeArgument.PipePath, instance);
} }
private static IMediaAnalysis ParseOutput(string filePath, Instance instance) private static IMediaAnalysis? ParseOutput(string filePath, Instance instance)
{ {
var json = string.Join(string.Empty, instance.OutputData); var json = string.Join(string.Empty, instance.OutputData);
var ffprobeAnalysis = JsonSerializer.Deserialize<FFProbeAnalysis>(json, new JsonSerializerOptions var ffprobeAnalysis = JsonSerializer.Deserialize<FFProbeAnalysis>(json, new JsonSerializerOptions
{ {
PropertyNameCaseInsensitive = true PropertyNameCaseInsensitive = true
})!; })!;
if (ffprobeAnalysis?.Format == null) return null;
return new MediaAnalysis(filePath, ffprobeAnalysis); return new MediaAnalysis(filePath, ffprobeAnalysis);
} }

View file

@ -8,6 +8,7 @@ namespace FFMpegCore
internal class MediaAnalysis : IMediaAnalysis internal class MediaAnalysis : IMediaAnalysis
{ {
private static readonly Regex DurationRegex = new Regex("^(\\d{1,2}:\\d{1,2}:\\d{1,2}(.\\d{1,7})?)", RegexOptions.Compiled); private static readonly Regex DurationRegex = new Regex("^(\\d{1,2}:\\d{1,2}:\\d{1,2}(.\\d{1,7})?)", RegexOptions.Compiled);
internal MediaAnalysis(string path, FFProbeAnalysis analysis) internal MediaAnalysis(string path, FFProbeAnalysis analysis)
{ {
Format = ParseFormat(analysis.Format); Format = ParseFormat(analysis.Format);
@ -27,14 +28,15 @@ private MediaFormat ParseFormat(Format analysisFormat)
FormatLongName = analysisFormat.FormatLongName, FormatLongName = analysisFormat.FormatLongName,
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,
}; };
} }
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 => new [] public TimeSpan Duration => new[]
{ {
Format.Duration, Format.Duration,
PrimaryVideoStream?.Duration ?? TimeSpan.Zero, PrimaryVideoStream?.Duration ?? TimeSpan.Zero,
@ -67,7 +69,8 @@ private VideoStream ParseVideoStream(FFProbeStream stream)
Profile = stream.Profile, Profile = stream.Profile,
PixelFormat = stream.PixelFormat, PixelFormat = stream.PixelFormat,
Rotation = (int)float.Parse(stream.GetRotate() ?? "0"), Rotation = (int)float.Parse(stream.GetRotate() ?? "0"),
Language = stream.GetLanguage() Language = stream.GetLanguage(),
Tags = stream.Tags,
}; };
} }
@ -77,6 +80,7 @@ private static TimeSpan ParseDuration(FFProbeStream ffProbeStream)
? TimeSpan.Parse(ffProbeStream.Duration) ? TimeSpan.Parse(ffProbeStream.Duration)
: TimeSpan.Parse(TrimTimeSpan(ffProbeStream.GetDuration()) ?? "0"); : TimeSpan.Parse(TrimTimeSpan(ffProbeStream.GetDuration()) ?? "0");
} }
private static string? TrimTimeSpan(string? durationTag) private static string? TrimTimeSpan(string? durationTag)
{ {
var durationMatch = DurationRegex.Match(durationTag ?? ""); var durationMatch = DurationRegex.Match(durationTag ?? "");
@ -96,17 +100,20 @@ private AudioStream ParseAudioStream(FFProbeStream stream)
Duration = ParseDuration(stream), Duration = ParseDuration(stream),
SampleRateHz = !string.IsNullOrEmpty(stream.SampleRate) ? ParseIntInvariant(stream.SampleRate) : default, SampleRateHz = !string.IsNullOrEmpty(stream.SampleRate) ? ParseIntInvariant(stream.SampleRate) : default,
Profile = stream.Profile, Profile = stream.Profile,
Language = stream.GetLanguage() Language = stream.GetLanguage(),
Tags = stream.Tags,
}; };
} }
private static double DivideRatio((double, double) ratio) => ratio.Item1 / ratio.Item2; private static double DivideRatio((double, double) ratio) => ratio.Item1 / ratio.Item2;
private static (int, int) ParseRatioInt(string input, char separator) private static (int, int) ParseRatioInt(string input, char separator)
{ {
if (string.IsNullOrEmpty(input)) return (0, 0); if (string.IsNullOrEmpty(input)) return (0, 0);
var ratio = input.Split(separator); var ratio = input.Split(separator);
return (ParseIntInvariant(ratio[0]), ParseIntInvariant(ratio[1])); return (ParseIntInvariant(ratio[0]), ParseIntInvariant(ratio[1]));
} }
private static (double, double) ParseRatioDouble(string input, char separator) private static (double, double) ParseRatioDouble(string input, char separator)
{ {
if (string.IsNullOrEmpty(input)) return (0, 0); if (string.IsNullOrEmpty(input)) return (0, 0);
@ -116,6 +123,7 @@ private static (double, double) ParseRatioDouble(string input, char separator)
private static double ParseDoubleInvariant(string line) => private static double ParseDoubleInvariant(string line) =>
double.Parse(line, System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.InvariantCulture); double.Parse(line, System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.InvariantCulture);
private static int ParseIntInvariant(string line) => private static int ParseIntInvariant(string line) =>
int.Parse(line, System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.InvariantCulture); int.Parse(line, System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.InvariantCulture);
} }

View file

@ -1,4 +1,5 @@
using System; using System;
using System.Collections.Generic;
namespace FFMpegCore namespace FFMpegCore
{ {
@ -10,5 +11,6 @@ public class MediaFormat
public int StreamCount { get; set; } public int StreamCount { get; set; }
public double ProbeScore { get; set; } public double ProbeScore { get; set; }
public double BitRate { get; set; } public double BitRate { get; set; }
public Dictionary<string, string>? Tags { get; set; }
} }
} }

View file

@ -1,5 +1,7 @@
using FFMpegCore.Enums; using FFMpegCore.Enums;
using System; using System;
using System.Collections.Generic;
namespace FFMpegCore namespace FFMpegCore
{ {
@ -11,6 +13,7 @@ public class MediaStream
public int BitRate { get; internal set; } 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 string? Language { get; internal set; }
public Dictionary<string, string>? Tags { get; internal set; }
public Codec GetCodecInfo() => FFMpeg.GetCodec(CodecName); public Codec GetCodecInfo() => FFMpeg.GetCodec(CodecName);
} }