/// <summary>
 /// Prepares the files of an existing job.
 /// </summary>
 /// <returns>A MediaEncoderSegments object containing the segments analysis.</returns>
 public MediaEncoderSegments PrepareExistingJob(MediaEncoderSettings settings) {
     settings.ResumePos = 0;
     settings.CompletionStatus = CompletionStatus.Success;
     if (File.Exists(settings.FinalFile)) {
         // Merging was completed.
         return null;
     } else {
         MediaEncoderSegments SegBusiness = new MediaEncoderSegments();
         SegBusiness.Analyze(settings);
         return SegBusiness;
     }
 }
        /// <summary>
        /// For files encoded in various segments (stop/resume), merge the various segments.
        /// </summary>
        private CompletionStatus MergeSegments(MediaEncoderSettings settings, string destination) {
            MediaEncoderSegments segBusiness = new MediaEncoderSegments();
            segBusiness.Analyze(settings);
            if (segBusiness.SegLeft.Count() > 0)
                return CompletionStatus.Error;

            List<string> SegmentList = new List<string>();
            foreach (SegmentInfo seg in segBusiness.SegDone) {
                SegmentList.Add(PathManager.GetOutputFile(settings.JobIndex, seg.Start, settings.Container));
            }

            CompletionStatus Result = CompletionStatus.Success;
            File.Delete(destination);
            if (SegmentList.Count == 1)
                File.Move(SegmentList[0], destination);
            else if (SegmentList.Count > 1) {
                Result = MediaMuxer.Concatenate(SegmentList, destination, new ProcessStartOptions(settings.JobIndex, "Merging Files", false));
            }
            settings.CompletionStatus = Result;
            return Result;
        }
        private void EncoderThread(object obj) {
            MediaEncoderSettings settings = obj as MediaEncoderSettings;
            settings.CompletionStatus = CompletionStatus.Success;
            DateTime StartTime = DateTime.Now;

            FFmpegConfig.UserInterfaceManager.Start(settings.JobIndex, "Processing Video");

            MediaEncoderSegments SegBusiness = PrepareExistingJob(settings);
            // If merging is completed, SegBusiness==null. If work is completed but not merged, SegBusiness.SegLeft = empty list.
            if (SegBusiness != null && SegBusiness.SegLeft.Count() > 0) {
                if (settings.Deshaker && (settings.DeshakerSettings.PrescanAction != PrescanType.Full || !settings.DeshakerSettings.PrescanCompleted)) {
                    settings.DeshakerSettings.PrescanAction = PrescanType.Full;
                    settings.CompletionStatus = GenerateDeshakerLog(settings, settings.InputFile);
                }

                if (settings.CompletionStatus == CompletionStatus.Success) {
                    // Encode audio stream
                    Task EncAudio = null;
                    if (settings.HasAudioOptions)
                        EncAudio = Task.Run(() => EncoderBusiness.EncodeAudio(settings));

                    // Encode video stream in segments
                    List<Task<CompletionStatus>> EncTasks = new List<Task<CompletionStatus>>();
                    if (settings.VideoAction != VideoAction.Copy) {
                        bool Cancel = false;
                        foreach (SegmentInfo seg in SegBusiness.SegLeft) {
                            MediaEncoderSettings EncSettings = settings.Clone();
                            EncSettings.ResumePos = seg.Start;
                            File.Delete(EncSettings.OutputFile);
                            EncTasks.Add(Task.Run(() => EncoderBusiness.EncodeVideo(EncSettings, seg.Length, SegBusiness.TotalFrames)));

                            // If there are more segments than max parallel instances, wait until some threads finish
                            if (EncTasks.Count >= settings.ParallelProcessing) {
                                Task.WaitAny(EncTasks.ToArray());
                                foreach (var item in EncTasks.ToArray()) {
                                    if (item.IsCompleted) {
                                        if (item.Result == CompletionStatus.Success)
                                            EncTasks.Remove(item);
                                        else {
                                            settings.CompletionStatus = item.Result;
                                            Cancel = true;
                                        }
                                    }
                                }
                            }
                            if (Cancel)
                                break;
                        }
                    }

                    EncAudio?.Wait();
                    Task.WaitAll(EncTasks.ToArray());
                }
            } else if (settings.CompletionStatus == CompletionStatus.None)
                settings.CompletionStatus = CompletionStatus.Success;

            if (FFmpegConfig.UserInterfaceManager.AppExited)
                return;

            // Check if encode is completed.
            EncodingCompletedEventArgs CompletedArgs = null;
            if (settings.CompletionStatus == CompletionStatus.Success) {
                CompletedArgs = FinalizeEncoding(settings, StartTime);
                if (settings.CompletionStatus == CompletionStatus.Success && CompletedArgs != null)
                    EncodingCompleted?.Invoke(this, CompletedArgs);

            }
            if (settings.CompletionStatus != CompletionStatus.Success) {
                CompletedArgs = GetEncodingResults(settings, null, StartTime);
                if (IsEncoding)
                    EncodingFailed?.Invoke(this, CompletedArgs);
            }

            if (IsEncoding || settings.CompletionStatus == CompletionStatus.Success)
                Application.Current.Dispatcher.Invoke(() => ProcessingQueue.Remove(settings));

            FFmpegConfig.UserInterfaceManager.Stop(settings.JobIndex);

            // Start next job.
            if (IsEncoding && ProcessingQueue.Count > 0)
                EncoderThread(ProcessingQueue.First());

            JobThread = null;
        }