public static GlobalExportProgress BuildFromRenderOptionsSingleCommand(ICollection <VideoRenderOption> videoRenderOptions, Action <string, double, double, double> progressChangedCallback)
        {
            var progress = new GlobalExportProgress(videoRenderOptions.Any(v => v.TimeWarpSettings.Any()) ? 2 : 1, progressChangedCallback);

            progress.StartExport();
            return(progress);
        }
        public static GlobalExportProgress BuildFromRenderOptionsPreEffect(ICollection <VideoRenderOption> videoRenderOptions, Action <string, double, double, double> progressChangedCallback)
        {
            // по одной операции для вырезания каждого эпизода.
            var totalOperationsExpected = videoRenderOptions.Count;

            // по одной операции для каждого эпизода с текстом.
            totalOperationsExpected += videoRenderOptions.Count(v => v.OverlayTextTimeTable != null && v.OverlayTextTimeTable.Any());

            // по одной операции для каждого эпизода со штрихами.
            totalOperationsExpected += videoRenderOptions.Count(v => v.ImagesTimeTable != null && v.ImagesTimeTable.Any());

            // по одной операции для каждого эффекта времени.
            totalOperationsExpected += videoRenderOptions.SelectMany(v => v.TimeWarpSettings ?? new List <TimeWarpRecord>()).Count();

            if (videoRenderOptions.Count > 1)
            {
                // один раз склеить и конвертировать эпизоды в конечный формат.
                totalOperationsExpected += 2;
            }

            var progress = new GlobalExportProgress(totalOperationsExpected, progressChangedCallback);

            progress.StartExport();
            return(progress);
        }
        public void StartRender(string outputFile, Size outputSize, ProcessPriorityClass processPriorityClass, Action <string, double, double, double> callbackAction = null, Action <double, Exception> finishAction = null)
        {
            var renderStart = DateTime.Now;

            // TODO: подкоректировать в соответствии с эксперементальными затратами на конвертацию.
            // Сейчас это вырезать эпизоды, нарисовать по ним текст+штрихи и в конце один раз все склеить.
            var globalExportProgress = GlobalExportProgress.BuildFromRenderOptionsSingleCommand(this.videoRenderOptions, callbackAction);

            try
            {
                try
                {
                    if (File.Exists(outputFile))
                    {
                        File.Delete(outputFile);
                    }

                    if (this.cancellationToken.IsCancellationRequested)
                    {
                        this.cancellationToken.ThrowIfCancellationRequested();
                    }

                    var episodesRenderer = new EpisodesRendererAllFiltersInSingleCommandsTextAsImage(this.videoRenderOptions,
                                                                                                     outputFile,
                                                                                                     outputSize,
                                                                                                     processPriorityClass,
                                                                                                     globalExportProgress,
                                                                                                     this.cancellationToken);
                    episodesRenderer.ProcessRenderOptions();

                    finishAction?.Invoke((DateTime.Now - renderStart).TotalMilliseconds, null);
                }
                catch (AggregateException aggregateException)
                {
                    var isAggregateAndAllInnerAreCancelledExceptions = aggregateException.InnerExceptions.All(e => e is FFMpegCancelledException);
                    if (isAggregateAndAllInnerAreCancelledExceptions)
                    {
                        var ffmpegOutputs     = aggregateException.InnerExceptions.Cast <FFMpegCancelledException>().Select(v => v.AllFFMpegOutput);
                        var aggregatedOutputs = ffmpegOutputs.Aggregate((t, c) => t + "\r\n" + c);
                        throw new FFMpegCancelledException("Process was cancelled", $"Aggregated exceptions:\r\n{aggregatedOutputs}");
                    }
                    throw;
                }
                catch (OperationCanceledException)
                {
                    throw new FFMpegCancelledException("Process was cancelled", "No FFMPeg log available. Cancelled between FFMPeg calls.");
                }
            }
            catch (Exception ex)
            {
                if (File.Exists(outputFile))
                {
                    File.Delete(outputFile);
                }
                finishAction?.Invoke((DateTime.Now - renderStart).TotalMilliseconds, ex);
            }
        }