public void ProcessRenderOptions() { using (var temporaryFilesStorage = new TemporaryFilesStorage()) { var subject = new Subject <double>(); // ReSharper disable once ImpureMethodCallOnReadonlyValueField // Внутри происходит регистрация через ссылку на родительский CancellationTokenSource. this.cancellationToken.Register(() => subject.OnNext(0)); using (var ffMpeg = new FFMpeg(temporaryFilesStorage, this.rendererProcessPriorityClass, subject.AsObservable())) { ffMpeg.LogMessage($"Started rendering of {this.outputFile}", string.Empty); var cutInfos = this.VideoRenderOptions.Select( v => { if (string.IsNullOrEmpty(v.FilePath)) { return(new FFMpegCutInfo(v.VideoStreamPath, v.AudioStreamPath, v.StartSecond, v.StartSecond + v.DurationSeconds, v.IsMuted)); } return(new FFMpegCutInfo(v.FilePath, v.StartSecond, v.StartSecond + v.DurationSeconds, v.IsMuted)); }).ToList(); this.CutAndConcatAndRenderTextAndImageAndTimeWarps(cutInfos, ffMpeg, temporaryFilesStorage); } } }
public byte[] GetFrameFromVideoAsByte(string videoFile, double positionMs, FFMpegImageSize imageSize) { using (var tempFileStorage = new TemporaryFilesStorage()) { using (var mhandler = new FFMpeg(tempFileStorage)) { return(mhandler.GetBitmapFromVideoAsByte(videoFile, positionMs, imageSize)); } } }
public async Task <byte[]> GetFrameFromVideoAsByteAsync(string videoFile, double positionMs, FFMpegImageSize imageSize) { return(await await Task.Factory.StartNew( async() => { using (var tempFileStorage = new TemporaryFilesStorage()) { using (var mhandler = new FFMpeg(tempFileStorage)) { return mhandler.GetBitmapFromVideoAsByte(videoFile, positionMs, imageSize); } } }, TaskCreationOptions.RunContinuationsAsynchronously)); }
public void ProcessRenderOptions() { using (var temporaryFilesStorage = new TemporaryFilesStorage()) { var subject = new Subject <double>(); // ReSharper disable once ImpureMethodCallOnReadonlyValueField // Внутри происходит регистрация через ссылку на родительский CancellationTokenSource. this.cancellationToken.Register(() => subject.OnNext(0)); using (var ffMpeg = new FFMpeg(temporaryFilesStorage, this.rendererProcessPriorityClass, subject.AsObservable())) { ffMpeg.LogMessage($"Started rendering of {this.outputFile}", string.Empty); var cutOptionsBuilder = new CutOptionsBuilder(this.videoRenderOptions, this.outputSize, this.globalExportProgress, temporaryFilesStorage, false); Parallel.ForEach( cutOptionsBuilder.CutOptions, cutOptions => { if (this.cancellationToken.IsCancellationRequested) { this.cancellationToken.ThrowIfCancellationRequested(); } // ReSharper disable once AccessToDisposedClosure // выполнение замыкания всегда будет происходить до Dispose. this.CutAndDrawTextAndDrawImageAndApplyTimeWarp(ffMpeg, cutOptions, temporaryFilesStorage); }); if (cutOptionsBuilder.FilesToConcat.Count == 1) { File.Move(cutOptionsBuilder.FilesToConcat.Single(), this.outputFile); } else { if (this.cancellationToken.IsCancellationRequested) { this.cancellationToken.ThrowIfCancellationRequested(); } var tempFileForConcat = temporaryFilesStorage.GetIntermediateFile(cutOptionsBuilder.OutputExtension); ffMpeg.Concat(tempFileForConcat, this.outputSize, "copy", "copy", this.globalExportProgress, cutOptionsBuilder.FilesToConcat.ToArray()); ffMpeg.Convert(tempFileForConcat, this.outputFile, this.globalExportProgress); } } } }
private void CutAndDrawTextAndDrawImageAndApplyTimeWarp( FFMpeg ffMpeg, FFMpegCutOptions cutOptions, TemporaryFilesStorage temporaryFilesStorage) { EnsureFileDoesNotExist(cutOptions.OutputFile); var extensionForResultFile = Path.GetExtension(cutOptions.OutputFile); var imagesExist = cutOptions.ImagesTimeTable != null && cutOptions.ImagesTimeTable.Any(); var timeWarpExists = cutOptions.TimeWarps != null && cutOptions.TimeWarps.Any(); if (!cutOptions.OverlayText.Any() && !imagesExist && !timeWarpExists) { ffMpeg.Cut(cutOptions); return; } var intermediateFile1 = temporaryFilesStorage.GetIntermediateFile(extensionForResultFile); ffMpeg.Cut(cutOptions.CloneWithOtherOutput(intermediateFile1)); if (cutOptions.OverlayText.Any()) { var intermediateFile2 = (imagesExist || timeWarpExists) ? temporaryFilesStorage.GetIntermediateFile(extensionForResultFile) : cutOptions.OutputFile; ffMpeg.DrawText(intermediateFile1, cutOptions.OverlayText, intermediateFile2, cutOptions.GlobalExportProgress); File.Delete(intermediateFile1); intermediateFile1 = intermediateFile2; } if (imagesExist) { var intermediateFile3 = timeWarpExists ? temporaryFilesStorage.GetIntermediateFile(extensionForResultFile) : cutOptions.OutputFile; ffMpeg.DrawImage(intermediateFile1, cutOptions.ImagesTimeTable, intermediateFile3, cutOptions.GlobalExportProgress); File.Delete(intermediateFile1); intermediateFile1 = intermediateFile3; } if (timeWarpExists) { ffMpeg.ApplyTimeWarp(intermediateFile1, cutOptions.TimeWarps, cutOptions.OutputFile, this.globalExportProgress); File.Delete(intermediateFile1); } }
protected override void CutAndConcatAndRenderTextAndImageAndTimeWarps(List <FFMpegCutInfo> cutInfos, FFMpeg ffMpeg, TemporaryFilesStorage temporaryFilesStorage) { foreach (var videoRenderOption in this.VideoRenderOptions) { if (videoRenderOption.OverlayTextTimeTable != null && videoRenderOption.OverlayTextTimeTable.Any()) { var textImages = videoRenderOption.OverlayTextTimeTable.Select( v => { const int FontSizeFor1024Width = 30; var fontSize = this.OutputSize.IsEmpty ? FontSizeFor1024Width : ((double)this.OutputSize.Width / 1024) * FontSizeFor1024Width; var image = StringUtils.DrawTextOnImage(v.Lines.Aggregate((t, c) => t + c), Color.Transparent, Color.DarkGreen, new Font(SystemFonts.DefaultFont.FontFamily, (int)fontSize), this.OutputSize.Width - 10); var left = this.OutputSize.Width / 2 - image.Width / 2; var top = this.OutputSize.Height - image.Height - 15; return(new DrawImageTimeRecord(image.ToBytes(ImageFormat.Bmp), left, top, v.StartSecond, v.EndSecond)); }); videoRenderOption.ImagesTimeTable.AddRange(textImages); videoRenderOption.OverlayTextTimeTable.Clear(); } } base.CutAndConcatAndRenderTextAndImageAndTimeWarps(cutInfos, ffMpeg, temporaryFilesStorage); }
protected virtual void CutAndConcatAndRenderTextAndImageAndTimeWarps(List <FFMpegCutInfo> cutInfos, FFMpeg ffMpeg, TemporaryFilesStorage temporaryFilesStorage) { var textTimeTableForConcatenatedEpisodeGroups = new List <TextTimeRecord>(); var imagesTimeTableForConcatenatedEpisodeGroups = new List <DrawImageTimeRecord>(); var timeWarpForConcatenatedEpisodeGroups = new List <TimeWarpRecord>(); var currentPosition = 0.0; foreach (var videoRenderOption in this.VideoRenderOptions) { if (videoRenderOption.OverlayTextTimeTable != null && videoRenderOption.OverlayTextTimeTable.Any()) { textTimeTableForConcatenatedEpisodeGroups.AddRange( videoRenderOption.OverlayTextTimeTable.Select(v => new TextTimeRecord(v.Lines, v.StartSecond + currentPosition, v.EndSecond + currentPosition))); } if (videoRenderOption.ImagesTimeTable != null && videoRenderOption.ImagesTimeTable.Any()) { imagesTimeTableForConcatenatedEpisodeGroups.AddRange( videoRenderOption.ImagesTimeTable.Select(v => new DrawImageTimeRecord(v.ImageData, v.LeftOffset, v.TopOffset, v.ImageStartSecond + currentPosition, v.ImageEndSecond + currentPosition))); } if (videoRenderOption.TimeWarpSettings != null && videoRenderOption.TimeWarpSettings.Any()) { timeWarpForConcatenatedEpisodeGroups.AddRange( videoRenderOption.TimeWarpSettings.Select(v => new TimeWarpRecord(v.StartSecond + currentPosition, v.EndSecond + currentPosition, v.Coefficient))); } currentPosition += videoRenderOption.DurationSeconds; } var pathForConcatenatedEpisodes = timeWarpForConcatenatedEpisodeGroups.Any() ? temporaryFilesStorage.GetIntermediateFile(Path.GetExtension(this.outputFile)) : this.outputFile; ffMpeg.CutAndConcatAndDrawImagesAndText( cutInfos, imagesTimeTableForConcatenatedEpisodeGroups, textTimeTableForConcatenatedEpisodeGroups, this.OutputSize, pathForConcatenatedEpisodes, this.globalExportProgress); if (timeWarpForConcatenatedEpisodeGroups.Any()) { ffMpeg.ApplyTimeWarp(pathForConcatenatedEpisodes, timeWarpForConcatenatedEpisodeGroups, this.outputFile, this.globalExportProgress); } }
public CutOptionsBuilder(IList <VideoRenderOption> videoRenderOptions, Size outputSize, IGlobalExportProgress globalExportProgress, TemporaryFilesStorage temporaryFilesStorage, bool ignoreOverlays) { this.temporaryFilesStorage = temporaryFilesStorage; this.BuildCutOptions(videoRenderOptions, outputSize, globalExportProgress, ignoreOverlays); }