Merge pull request #226 from rosenbjerg/master

v.4.4.0
This commit is contained in:
Malte Rosenbjerg 2021-07-16 01:21:52 +02:00 committed by GitHub
commit 7ebb47d757
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 114 additions and 7 deletions

View file

@ -12,6 +12,7 @@
using FFMpegCore.Arguments; using FFMpegCore.Arguments;
using FFMpegCore.Exceptions; using FFMpegCore.Exceptions;
using FFMpegCore.Pipes; using FFMpegCore.Pipes;
using System.Threading;
namespace FFMpegCore.Test namespace FFMpegCore.Test
{ {
@ -612,5 +613,64 @@ public async Task Video_Cancel_Async_With_Timeout()
Assert.AreEqual("h264", outputInfo.PrimaryVideoStream.CodecName); Assert.AreEqual("h264", outputInfo.PrimaryVideoStream.CodecName);
Assert.AreEqual("aac", outputInfo.PrimaryAudioStream!.CodecName); Assert.AreEqual("aac", outputInfo.PrimaryAudioStream!.CodecName);
} }
[TestMethod, Timeout(10000)]
public async Task Video_Cancel_CancellationToken_Async()
{
var outputFile = new TemporaryFile("out.mp4");
var cts = new CancellationTokenSource();
var task = FFMpegArguments
.FromFileInput("testsrc2=size=320x240[out0]; sine[out1]", false, args => args
.WithCustomArgument("-re")
.ForceFormat("lavfi"))
.OutputToFile(outputFile, false, opt => opt
.WithAudioCodec(AudioCodec.Aac)
.WithVideoCodec(VideoCodec.LibX264)
.WithSpeedPreset(Speed.VeryFast))
.CancellableThrough(cts.Token)
.ProcessAsynchronously(false);
await Task.Delay(300);
cts.Cancel();
var result = await task;
Assert.IsFalse(result);
}
[TestMethod, Timeout(10000)]
public async Task Video_Cancel_CancellationToken_Async_With_Timeout()
{
var outputFile = new TemporaryFile("out.mp4");
var cts = new CancellationTokenSource();
var task = FFMpegArguments
.FromFileInput("testsrc2=size=320x240[out0]; sine[out1]", false, args => args
.WithCustomArgument("-re")
.ForceFormat("lavfi"))
.OutputToFile(outputFile, false, opt => opt
.WithAudioCodec(AudioCodec.Aac)
.WithVideoCodec(VideoCodec.LibX264)
.WithSpeedPreset(Speed.VeryFast))
.CancellableThrough(cts.Token, 5000)
.ProcessAsynchronously(false);
await Task.Delay(300);
cts.Cancel();
var result = await task;
var outputInfo = await FFProbe.AnalyseAsync(outputFile);
Assert.IsTrue(result);
Assert.IsNotNull(outputInfo);
Assert.AreEqual(320, outputInfo.PrimaryVideoStream!.Width);
Assert.AreEqual(240, outputInfo.PrimaryVideoStream.Height);
Assert.AreEqual("h264", outputInfo.PrimaryVideoStream.CodecName);
Assert.AreEqual("aac", outputInfo.PrimaryAudioStream!.CodecName);
}
} }
} }

View file

@ -50,6 +50,11 @@ public FFMpegArgumentProcessor CancellableThrough(out Action cancel, int timeout
cancel = () => CancelEvent?.Invoke(this, timeout); cancel = () => CancelEvent?.Invoke(this, timeout);
return this; return this;
} }
public FFMpegArgumentProcessor CancellableThrough(CancellationToken token, int timeout = 0)
{
token.Register(() => CancelEvent?.Invoke(this, timeout));
return this;
}
public bool ProcessSynchronously(bool throwOnError = true, FFOptions? ffMpegOptions = null) public bool ProcessSynchronously(bool throwOnError = true, FFOptions? ffMpegOptions = null)
{ {
using var instance = PrepareInstance(ffMpegOptions ?? GlobalFFOptions.Current, out var cancellationTokenSource); using var instance = PrepareInstance(ffMpegOptions ?? GlobalFFOptions.Current, out var cancellationTokenSource);

View file

@ -9,10 +9,11 @@
<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>- Added support for PCM audio through RawAudioPipeSource (thanks to Namaneo) <PackageReleaseNotes>- Cancellation token support (thanks patagonaa)
- Removed -y in InputPipeArgument due to reported problems</PackageReleaseNotes> - Support for setting stdout and stderr encoding for ffprobe (thanks CepheiSigma)
- Improved ffprobe exceptions</PackageReleaseNotes>
<LangVersion>8</LangVersion> <LangVersion>8</LangVersion>
<PackageVersion>4.3.0</PackageVersion> <PackageVersion>4.4.0</PackageVersion>
<PackageLicenseExpression>MIT</PackageLicenseExpression> <PackageLicenseExpression>MIT</PackageLicenseExpression>
<Authors>Malte Rosenbjerg, Vlad Jerca, Max Bagryantsev</Authors> <Authors>Malte Rosenbjerg, Vlad Jerca, Max Bagryantsev</Authors>
<PackageTags>ffmpeg ffprobe convert video audio mediafile resize analyze muxing</PackageTags> <PackageTags>ffmpeg ffprobe convert video audio mediafile resize analyze muxing</PackageTags>

View file

@ -0,0 +1,11 @@
using System;
namespace FFMpegCore.Exceptions
{
public class FFProbeException : Exception
{
public FFProbeException(string message, Exception? inner = null) : base(message, inner)
{
}
}
}

View file

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
namespace FFMpegCore.Exceptions
{
public class FFProbeProcessException : FFProbeException
{
public IReadOnlyCollection<string> ProcessErrors { get; }
public FFProbeProcessException(string message, IReadOnlyCollection<string> processErrors, Exception? inner = null) : base(message, inner)
{
ProcessErrors = processErrors;
}
}
}

View file

@ -0,0 +1,9 @@
namespace FFMpegCore.Exceptions
{
public class FormatNullException : FFProbeException
{
public FormatNullException() : base("Format not specified")
{
}
}
}

View file

@ -1,4 +1,5 @@
using System; using System;
using System.Diagnostics;
using System.IO; using System.IO;
using System.Text.Json; using System.Text.Json;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -92,7 +93,7 @@ public static async Task<IMediaAnalysis> AnalyseAsync(Stream stream, int outputC
} }
var exitCode = await task.ConfigureAwait(false); var exitCode = await task.ConfigureAwait(false);
if (exitCode != 0) if (exitCode != 0)
throw new FFMpegException(FFMpegExceptionType.Process, $"FFProbe process returned exit status {exitCode}", null, string.Join("\n", instance.ErrorData)); throw new FFProbeProcessException($"ffprobe exited with non-zero exit-code ({exitCode} - {string.Join("\n", instance.ErrorData)})", instance.ErrorData);
pipeArgument.Post(); pipeArgument.Post();
return ParseOutput(instance); return ParseOutput(instance);
@ -107,7 +108,7 @@ private static IMediaAnalysis ParseOutput(Instance instance)
}); });
if (ffprobeAnalysis?.Format == null) if (ffprobeAnalysis?.Format == null)
throw new Exception(); throw new FormatNullException();
return new MediaAnalysis(ffprobeAnalysis); return new MediaAnalysis(ffprobeAnalysis);
} }
@ -117,7 +118,12 @@ private static Instance PrepareInstance(string filePath, int outputCapacity, FFO
FFProbeHelper.RootExceptionCheck(); FFProbeHelper.RootExceptionCheck();
FFProbeHelper.VerifyFFProbeExists(ffOptions); FFProbeHelper.VerifyFFProbeExists(ffOptions);
var arguments = $"-loglevel error -print_format json -show_format -sexagesimal -show_streams \"{filePath}\""; var arguments = $"-loglevel error -print_format json -show_format -sexagesimal -show_streams \"{filePath}\"";
var instance = new Instance(GlobalFFOptions.GetFFProbeBinaryPath(), arguments) {DataBufferCapacity = outputCapacity}; var startInfo = new ProcessStartInfo(GlobalFFOptions.GetFFProbeBinaryPath(), arguments)
{
StandardOutputEncoding = ffOptions.Encoding,
StandardErrorEncoding = ffOptions.Encoding
};
var instance = new Instance(startInfo) { DataBufferCapacity = outputCapacity };
return instance; return instance;
} }
} }

View file

@ -30,7 +30,7 @@ public static void VerifyFFProbeExists(FFOptions ffMpegOptions)
var (exitCode, _) = Instance.Finish(GlobalFFOptions.GetFFProbeBinaryPath(ffMpegOptions), "-version"); var (exitCode, _) = Instance.Finish(GlobalFFOptions.GetFFProbeBinaryPath(ffMpegOptions), "-version");
_ffprobeVerified = exitCode == 0; _ffprobeVerified = exitCode == 0;
if (!_ffprobeVerified) if (!_ffprobeVerified)
throw new FFMpegException(FFMpegExceptionType.Operation, "ffprobe was not found on your system"); throw new FFProbeException("ffprobe was not found on your system");
} }
} }
} }