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 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 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); } } } }