private static Size?PrepareSnapshotSize(IMediaAnalysis source, Size?wantedSize) { if (wantedSize == null || (wantedSize.Value.Height <= 0 && wantedSize.Value.Width <= 0) || source.PrimaryVideoStream == null) { return(null); } var currentSize = new Size(source.PrimaryVideoStream.Width, source.PrimaryVideoStream.Height); if (source.PrimaryVideoStream.Rotation == 90 || source.PrimaryVideoStream.Rotation == 180) { currentSize = new Size(source.PrimaryVideoStream.Height, source.PrimaryVideoStream.Width); } if (wantedSize.Value.Width != currentSize.Width || wantedSize.Value.Height != currentSize.Height) { if (wantedSize.Value.Width <= 0 && wantedSize.Value.Height > 0) { var ratio = (double)wantedSize.Value.Height / currentSize.Height; return(new Size((int)(currentSize.Width * ratio), (int)(currentSize.Height * ratio))); } if (wantedSize.Value.Height <= 0 && wantedSize.Value.Width > 0) { var ratio = (double)wantedSize.Value.Width / currentSize.Width; return(new Size((int)(currentSize.Width * ratio), (int)(currentSize.Height * ratio))); } return(wantedSize); } return(null); }
private static FFMpegArguments BuildSnapshotArguments(IMediaAnalysis source, Size?size = null, TimeSpan?captureTime = null) { captureTime ??= TimeSpan.FromSeconds(source.Duration.TotalSeconds / 3); size = PrepareSnapshotSize(source, size); return(FFMpegArguments .FromSeekedFiles((source.Path, captureTime ?? TimeSpan.Zero)) .WithVideoCodec(VideoCodec.Png) .WithFrameOutputCount(1) .Resize(size)); }
/// <summary> /// Saves a 'png' thumbnail to an in-memory bitmap /// </summary> /// <param name="source">Source video file.</param> /// <param name="captureTime">Seek position where the thumbnail should be taken.</param> /// <param name="size">Thumbnail size. If width or height equal 0, the other will be computed automatically.</param> /// <returns>Bitmap with the requested snapshot.</returns> public static async Task <Bitmap> SnapshotAsync(IMediaAnalysis source, Size?size = null, TimeSpan?captureTime = null) { var(arguments, outputOptions) = BuildSnapshotArguments(source, size, captureTime); using var ms = new MemoryStream(); await arguments .OutputToPipe(new StreamPipeSink(ms), options => outputOptions(options .ForceFormat("rawvideo"))) .ProcessAsynchronously(); ms.Position = 0; return(new Bitmap(ms)); }
/// <summary> /// Saves a 'png' thumbnail from the input video to drive /// </summary> /// <param name="source">Source video analysis</param> /// <param name="output">Output video file path</param> /// <param name="captureTime">Seek position where the thumbnail should be taken.</param> /// <param name="size">Thumbnail size. If width or height equal 0, the other will be computed automatically.</param> /// <returns>Bitmap with the requested snapshot.</returns> public static Task <bool> SnapshotAsync(IMediaAnalysis source, string output, Size?size = null, TimeSpan?captureTime = null) { if (Path.GetExtension(output) != FileExtension.Png) { output = Path.GetFileNameWithoutExtension(output) + FileExtension.Png; } var(arguments, outputOptions) = BuildSnapshotArguments(source, size, captureTime); return(arguments .OutputToFile(output, true, outputOptions) .ProcessAsynchronously()); }
/// <summary> /// Saves a 'png' thumbnail to an in-memory bitmap /// </summary> /// <param name="source">Source video file.</param> /// <param name="captureTime">Seek position where the thumbnail should be taken.</param> /// <param name="size">Thumbnail size. If width or height equal 0, the other will be computed automatically.</param> /// <returns>Bitmap with the requested snapshot.</returns> public static Bitmap Snapshot(IMediaAnalysis source, Size?size = null, TimeSpan?captureTime = null) { var(arguments, outputOptions) = BuildSnapshotArguments(source, size, captureTime); using var ms = new MemoryStream(); arguments .OutputToPipe(new StreamPipeSink(ms), options => outputOptions(options .ForceFormat("rawvideo"))) .ProcessSynchronously(); ms.Position = 0; using var bitmap = new Bitmap(ms); return(bitmap.Clone(new Rectangle(0, 0, bitmap.Width, bitmap.Height), bitmap.PixelFormat)); }
/// <summary> /// Saves a 'png' thumbnail to an in-memory bitmap /// </summary> /// <param name="source">Source video file.</param> /// <param name="captureTime">Seek position where the thumbnail should be taken.</param> /// <param name="size">Thumbnail size. If width or height equal 0, the other will be computed automatically.</param> /// <returns>Bitmap with the requested snapshot.</returns> public static Bitmap Snapshot(IMediaAnalysis source, Size?size = null, TimeSpan?captureTime = null) { var arguments = BuildSnapshotArguments(source, size, captureTime); using var ms = new MemoryStream(); arguments .ForceFormat("rawvideo") .OutputToPipe(new StreamPipeSink(ms)) .ProcessSynchronously(); ms.Position = 0; return(new Bitmap(ms)); }
private static (FFMpegArguments, Action <FFMpegArgumentOptions> outputOptions) BuildSnapshotArguments( string input, IMediaAnalysis source, Size?size = null, TimeSpan?captureTime = null, int?streamIndex = null, int inputFileIndex = 0) { captureTime ??= TimeSpan.FromSeconds(source.Duration.TotalSeconds / 3); size = PrepareSnapshotSize(source, size); streamIndex = streamIndex == null ? 0 : source.VideoStreams.FirstOrDefault(videoStream => videoStream.Index == streamIndex).Index; return(FFMpegArguments .FromFileInput(input, false, options => options .Seek(captureTime)), options => options .SelectStream((int)streamIndex, inputFileIndex) .WithVideoCodec(VideoCodec.Png) .WithFrameOutputCount(1) .Resize(size)); }
private async Task GenerateFrames(IMediaAnalysis result) { while (!_cts.IsCancellationRequested) { var sink = new RawImagePipeSink(_videoSize, 3, OnFrame); var args = FFMpegArguments .FromFileInput(_filePath).OutputToPipe(sink, options => { options.DisableChannel(Channel.Audio) .UsingMultithreading(true) .ForceFormat("rawvideo") .ForcePixelFormat("bgr24"); if (_overrideSize.HasValue) { options.Resize(_videoSize); } }) .CancellableThrough(_cts.Token); //fire and forget await args.ProcessAsynchronously(true); } }
/// <summary> /// Convert a video do a different format. /// </summary> /// <param name="source">Input video source.</param> /// <param name="output">Output information.</param> /// <param name="type">Target conversion video type.</param> /// <param name="speed">Conversion target speed/quality (faster speed = lower quality).</param> /// <param name="size">Video size.</param> /// <param name="audioQuality">Conversion target audio quality.</param> /// <param name="multithreaded">Is encoding multithreaded.</param> /// <returns>Output video information.</returns> public static bool Convert( IMediaAnalysis source, string output, ContainerFormat format, Speed speed = Speed.SuperFast, VideoSize size = VideoSize.Original, AudioQuality audioQuality = AudioQuality.Normal, bool multithreaded = false) { FFMpegHelper.ExtensionExceptionCheck(output, format.Extension); FFMpegHelper.ConversionSizeExceptionCheck(source); var scale = VideoSize.Original == size ? 1 : (double)source.PrimaryVideoStream.Height / (int)size; var outputSize = new Size((int)(source.PrimaryVideoStream.Width / scale), (int)(source.PrimaryVideoStream.Height / scale)); if (outputSize.Width % 2 != 0) { outputSize.Width += 1; } return(format.Name switch { "mp4" => FFMpegArguments .FromFileInput(source) .OutputToFile(output, true, options => options .UsingMultithreading(multithreaded) .WithVideoCodec(VideoCodec.LibX264) .WithVideoBitrate(2400) .Scale(outputSize) .WithSpeedPreset(speed) .WithAudioCodec(AudioCodec.Aac) .WithAudioBitrate(audioQuality)) .ProcessSynchronously(), "ogv" => FFMpegArguments .FromFileInput(source) .OutputToFile(output, true, options => options .UsingMultithreading(multithreaded) .WithVideoCodec(VideoCodec.LibTheora) .WithVideoBitrate(2400) .Scale(outputSize) .WithSpeedPreset(speed) .WithAudioCodec(AudioCodec.LibVorbis) .WithAudioBitrate(audioQuality)) .ProcessSynchronously(), "mpegts" => FFMpegArguments .FromFileInput(source) .OutputToFile(output, true, options => options .CopyChannel() .WithBitStreamFilter(Channel.Video, Filter.H264_Mp4ToAnnexB) .ForceFormat(VideoType.Ts)) .ProcessSynchronously(), "webm" => FFMpegArguments .FromFileInput(source) .OutputToFile(output, true, options => options .UsingMultithreading(multithreaded) .WithVideoCodec(VideoCodec.LibVpx) .WithVideoBitrate(2400) .Scale(outputSize) .WithSpeedPreset(speed) .WithAudioCodec(AudioCodec.LibVorbis) .WithAudioBitrate(audioQuality)) .ProcessSynchronously(), _ => throw new ArgumentOutOfRangeException(nameof(format)) });
private static (FFMpegArguments, Action <FFMpegArgumentOptions> outputOptions) BuildSnapshotArguments(IMediaAnalysis source, Size?size = null, TimeSpan?captureTime = null) { captureTime ??= TimeSpan.FromSeconds(source.Duration.TotalSeconds / 3); size = PrepareSnapshotSize(source, size); return(FFMpegArguments .FromFileInput(source, options => options .Seek(captureTime)), options => options .WithVideoCodec(VideoCodec.Png) .WithFrameOutputCount(1) .Resize(size)); }
public static void ConversionSizeExceptionCheck(IMediaAnalysis info) { ConversionSizeExceptionCheck(new Size(info.PrimaryVideoStream.Width, info.PrimaryVideoStream.Height)); }
public FFMpegArguments AddFileInput(IMediaAnalysis mediaAnalysis, Action <FFMpegArgumentOptions>?addArguments = null) => WithInput(new InputArgument(mediaAnalysis.Path, false), addArguments);
public static FFMpegArguments FromFileInput(IMediaAnalysis mediaAnalysis, Action <FFMpegArgumentOptions>?addArguments = null) => new FFMpegArguments().WithInput(new InputArgument(mediaAnalysis.Path, false), addArguments);