diff --git a/FFMpegCore.Test/FFProbeTests.cs b/FFMpegCore.Test/FFProbeTests.cs index d66d561..5e877ff 100644 --- a/FFMpegCore.Test/FFProbeTests.cs +++ b/FFMpegCore.Test/FFProbeTests.cs @@ -31,5 +31,30 @@ public void Probe_Success() Assert.AreEqual(13, info.Duration.Seconds); } + + [TestMethod] + public void Probe_Success_FromStream() + { + var output = new FFProbe(); + + using (var stream = File.OpenRead(VideoLibrary.LocalVideo.FullName)) + { + var info = output.ParseVideoInfo(stream); + Assert.AreEqual(13, info.Duration.Seconds); + } + } + + [TestMethod] + public void Probe_Success_FromStream_Async() + { + var output = new FFProbe(); + + using (var stream = File.OpenRead(VideoLibrary.LocalVideo.FullName)) + { + var info = output.ParseVideoInfoAsync(stream).WaitForResult(); + + Assert.AreEqual(13, info.Duration.Seconds); + } + } } } \ No newline at end of file diff --git a/FFMpegCore.Test/TasksExtensions.cs b/FFMpegCore.Test/TasksExtensions.cs new file mode 100644 index 0000000..7958ec9 --- /dev/null +++ b/FFMpegCore.Test/TasksExtensions.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; + +namespace FFMpegCore.Test +{ + static class TasksExtensions + { + public static T WaitForResult(this Task task) + { + task.Wait(); + return task.Result; + } + } +} diff --git a/FFMpegCore/FFMPEG/Argument/Atoms/InputPipeArgument.cs b/FFMpegCore/FFMPEG/Argument/Atoms/InputPipeArgument.cs index bed5897..a45937b 100644 --- a/FFMpegCore/FFMPEG/Argument/Atoms/InputPipeArgument.cs +++ b/FFMpegCore/FFMPEG/Argument/Atoms/InputPipeArgument.cs @@ -16,6 +16,7 @@ namespace FFMpegCore.FFMPEG.Argument public class InputPipeArgument : Argument { public string PipeName { get; private set; } + public string PipePath => $@"\\.\pipe\{PipeName}"; public IPipeSource Source { get; private set; } private NamedPipeServerStream pipe; @@ -42,7 +43,7 @@ public void ClosePipe() public override string GetStringValue() { - return $"-y {Source.GetFormat()} -i \\\\.\\pipe\\{PipeName}"; + return $"-y {Source.GetFormat()} -i {PipePath}"; } public void FlushPipe() diff --git a/FFMpegCore/FFMPEG/FFProbe.cs b/FFMpegCore/FFMPEG/FFProbe.cs index 827ea7f..7c683bf 100644 --- a/FFMpegCore/FFMPEG/FFProbe.cs +++ b/FFMpegCore/FFMPEG/FFProbe.cs @@ -5,6 +5,8 @@ using System.Globalization; using System.Threading.Tasks; using Instances; +using FFMpegCore.FFMPEG.Argument; +using FFMpegCore.FFMPEG.Pipes; namespace FFMpegCore.FFMPEG { @@ -65,6 +67,56 @@ public async Task ParseVideoInfoAsync(VideoInfo info) return ParseVideoInfoInternal(info, output); } + public VideoInfo ParseVideoInfo(System.IO.Stream stream) + { + var info = new VideoInfo(); + var streamPipeSource = new StreamPipeSource(stream); + var pipeArgument = new InputPipeArgument(streamPipeSource); + + var instance = new Instance(_ffprobePath, BuildFFProbeArguments(pipeArgument.PipePath)) { DataBufferCapacity = _outputCapacity }; + pipeArgument.OpenPipe(); + + try + { + var task = instance.FinishedRunning(); + pipeArgument.FlushPipe(); + pipeArgument.ClosePipe(); + task.Wait(); + } + finally + { + pipeArgument.ClosePipe(); + } + + var output = string.Join("", instance.OutputData); + return ParseVideoInfoInternal(info, output); + } + + public async Task ParseVideoInfoAsync(System.IO.Stream stream) + { + var info = new VideoInfo(); + var streamPipeSource = new StreamPipeSource(stream); + var pipeArgument = new InputPipeArgument(streamPipeSource); + + var instance = new Instance(_ffprobePath, BuildFFProbeArguments(pipeArgument.PipePath)) { DataBufferCapacity = _outputCapacity }; + pipeArgument.OpenPipe(); + + try + { + var task = instance.FinishedRunning(); + await pipeArgument.FlushPipeAsync(); + pipeArgument.ClosePipe(); + await task; + } + finally + { + pipeArgument.ClosePipe(); + } + + var output = string.Join("", instance.OutputData); + return ParseVideoInfoInternal(info, output); + } + private static string BuildFFProbeArguments(string fullPath) => $"-v quiet -print_format json -show_streams \"{fullPath}\""; diff --git a/FFMpegCore/VideoInfo.cs b/FFMpegCore/VideoInfo.cs index f56145c..4e6c226 100644 --- a/FFMpegCore/VideoInfo.cs +++ b/FFMpegCore/VideoInfo.cs @@ -10,6 +10,10 @@ public class VideoInfo { private FileInfo _file; + internal VideoInfo() + { + + } /// /// Create a video information object from a file information object. /// @@ -76,37 +80,37 @@ public VideoInfo(string path, int outputCapacity = int.MaxValue) : this(new File /// /// Gets the name of the file. /// - public string Name => _file.Name; + public string Name => _file != null ? _file.Name : throw new FileNotFoundException(); /// /// Gets the full path of the file. /// - public string FullName => _file.FullName; + public string FullName => _file != null ? _file.FullName : throw new FileNotFoundException(); /// /// Gets the file extension. /// - public string Extension => _file.Extension; + public string Extension => _file != null ? _file.Extension : throw new FileNotFoundException(); /// /// Gets a flag indicating if the file is read-only. /// - public bool IsReadOnly => _file.IsReadOnly; + public bool IsReadOnly => _file != null ? _file.IsReadOnly : throw new FileNotFoundException(); /// /// Gets a flag indicating if the file exists (no cache, per call verification). /// - public bool Exists => File.Exists(FullName); + public bool Exists => _file != null ? File.Exists(FullName) : false; /// /// Gets the creation date. /// - public DateTime CreationTime => _file.CreationTime; + public DateTime CreationTime => _file != null ? _file.CreationTime : throw new FileNotFoundException(); /// /// Gets the parent directory information. /// - public DirectoryInfo Directory => _file.Directory; + public DirectoryInfo Directory => _file != null ? _file.Directory : throw new FileNotFoundException(); /// /// Create a video information object from a file information object.