private static void ApplyDegrain(AviSynthScriptBuilder Script, MediaEncoderSettings settings, ref bool isLsb, ref bool isYV24) { Script.LoadPluginAvsi("smdegrain.avsi"); Script.LoadPluginDll("MVTools2.dll"); Script.LoadPluginDll("MedianBlur2.dll"); if (settings.DegrainSharp) { Script.LoadPluginDll("rgtools.dll"); } else { Script.LoadPluginDll("RemoveGrain.dll"); } if (isYV24) { if (isLsb) { Script.AppendLine(@"Dither_resize16nr(Width, Height/2, kernel=""Spline36"", csp=""YV12"")"); } else { Script.AppendLine("ConvertToYV12()"); } isYV24 = false; } Script.AppendLine("SMDegrain(thsad={0}, prefilter={1}{2}{3})", settings.DegrainStrength * 10, settings.DegrainPrefilter == DegrainPrefilters.SD ? 1 : settings.DegrainPrefilter == DegrainPrefilters.HD ? 2 : 4, settings.DegrainSharp ? ", contrasharp=true" : "", isLsb ? ", lsb_in=true, lsb_out=true" : ""); }
/// <summary> /// Returns the code for the MatrixIn argument based on SourceColorMatrix. /// </summary> private static string GetMatrixIn(MediaEncoderSettings settings) { string Result = null; if (settings.SourceColorMatrix == ColorMatrix.Rec601) { Result = "Rec601"; } else if (settings.SourceColorMatrix == ColorMatrix.Pc601) { Result = "Pc601"; } else if (settings.SourceColorMatrix == ColorMatrix.Pc709) { Result = "Pc709"; } if (Result != null) { return(string.Format(@", MatrixIn=""{0}""", Result)); } else { return(""); } }
public void PrepareJobFiles(MediaEncoderSettings settings) { settings.JobIndex = ++JobIndex; // Files must be prepared before adding to queue so that user can replace preview files. PathManager.DeleteJobFiles(settings.JobIndex); File.Delete(PathManager.PreviewScriptFile); File.Delete(PathManager.PreviewSettingsFile); if (settings.ConvertToAvi) { PathManager.SafeMove(PathManager.PreviewSourceFile, settings.InputFile); } if (settings.Deshaker) { PathManager.SafeMove(PathManager.PreviewDeshakerLog, settings.DeshakerLog); } settings.Save(settings.SettingsFile); AviSynthScriptBuilder Script = new AviSynthScriptBuilder(settings.CustomScript); if (Script.IsEmpty) { Script = MediaEncoderScript.GenerateVideoScript(settings, settings.InputFile, false, true); } else { Script.Replace(Script.GetAsciiPath(PathManager.PreviewSourceFile), Script.GetAsciiPath(settings.InputFile)); Script.Replace(Script.GetAsciiPath(PathManager.PreviewDeshakerLog), Script.GetAsciiPath(settings.DeshakerLog)); } Script.WriteToFile(settings.ScriptFile); // if (settings.DeshakerSettings.PrescanAction == PrescanType.Full) }
private EncodingCompletedEventArgs GetEncodingResults(MediaEncoderSettings settings, string finalFile, DateTime?startTime) { // Create encoding result object. EncodingCompletedEventArgs Result = null; if (finalFile == null || File.Exists(finalFile)) { Result = new EncodingCompletedEventArgs(); Result.Settings = settings; Result.OldFileName = settings.FilePath; Result.NewFileName = settings.FinalFile; if (startTime.HasValue) { Result.EncodingTime = DateTime.Now - startTime.Value; } if (finalFile != null) { FileInfo FinalFileInfo = new FileInfo(finalFile); Result.NewFileSize = FinalFileInfo.Length; FinalFileInfo = new FileInfo(settings.FilePath); Result.OldFileSize = FinalFileInfo.Length; } } return(Result); }
/// <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); }
/// <summary> /// Moves specified settings file as preview files. /// </summary> /// <param name="settings">The settings to use for re-encoding.</param> public async Task MovePreviewFilesAsync(MediaEncoderSettings settings) { await DeletePreviewFilesAsync(); if (settings.ConvertToAvi) { File.Move(settings.InputFile, PathManager.PreviewSourceFile); } if (settings.Deshaker && File.Exists(settings.DeshakerLog)) { File.Move(settings.DeshakerLog, PathManager.PreviewDeshakerLog); } if (!string.IsNullOrEmpty(settings.CustomScript) && (settings.ConvertToAvi || settings.Deshaker)) { AviSynthScriptBuilder Script = new AviSynthScriptBuilder(settings.CustomScript); if (settings.ConvertToAvi) { Script.Replace(Script.GetAsciiPath(settings.InputFile), Script.GetAsciiPath(PathManager.PreviewSourceFile)); } if (settings.Deshaker) { Script.Replace(Script.GetAsciiPath(settings.DeshakerLog), Script.GetAsciiPath(PathManager.PreviewDeshakerLog)); } settings.CustomScript = Script.Script; } settings.Save(PathManager.PreviewSettingsFile); }
public void AddJobToQueue(MediaEncoderSettings settings) { ProcessingQueue.Add(settings); if (IsEncoding && ProcessingQueue.Count == 1) { StartEncoderThread(); } }
/// <summary> /// Prepares the files of an existing job that we resume. /// </summary> /// <returns>Whether job still needs to execute.</returns> //private bool PrepareResumeJob(MediaEncoderSettings settings) { // AvisynthTools.EditStartPosition(settings.ScriptFile, 0); // // At least one segment has been processed. Check if the entire file has been processed. // ProcessStartOptions Options = new ProcessStartOptions(settings.JobIndex, "Resuming...", false).TrackProcess(settings); // Task<long> TaskCount = Task.Run(() => AvisynthTools.GetFrameCount(settings.ScriptFile, Options)); // int Segment = 0; // List<Task<long>> TaskList = new List<Task<long>>(); // while (File.Exists(PathManager.GetOutputFile(settings.JobIndex, ++Segment, settings.VideoCodec)) && settings.CompletionStatus != CompletionStatus.Cancelled) { // string SegmentFile = PathManager.GetOutputFile(settings.JobIndex, Segment, settings.VideoCodec); // // Discard segments of less than 10kb. // if (new FileInfo(SegmentFile).Length > 10000) { // int SegmentLocal = Segment; // TaskList.Add(Task.Run(() => AvisynthTools.GetFrameCount(PathManager.GetOutputFile(settings.JobIndex, SegmentLocal, settings.VideoCodec), null))); // } else { // // There shouldn't be any resumed job following a segment that is being deleted. // File.Delete(SegmentFile); // break; // } // } // long OutputFrames = 0; // Task.WaitAll(TaskList.ToArray()); // foreach (Task<long> item in TaskList) { // OutputFrames += item.Result; // } // TaskCount.Wait(); // if (settings.CompletionStatus == CompletionStatus.Cancelled) // return false; // long ScriptFrames = TaskCount.Result; // if (OutputFrames >= ScriptFrames) { // // Job completed. // //EncodingCompletedEventArgs EncodeResult = FinalizeEncoding(settings, null); // //if (EncodeResult != null) // // EncodingCompleted(this, EncodeResult); // //else { // // PathManager.DeleteJobFiles(settings.JobIndex); // //} // return false; // } else { // // Resume with new segment. // AvisynthTools.EditStartPosition(settings.ScriptFile, OutputFrames); // settings.ResumeSegment = Segment; // settings.ResumePos = OutputFrames; // File.Delete(settings.OutputFile); // return true; // } //} private async Task GetMediaInfo(string previewFile, MediaEncoderSettings settings) { FFmpegProcess FInfo = await Task.Run(() => MediaInfo.GetFileInfo(previewFile)); FFmpegVideoStreamInfo VInfo = FInfo.VideoStream; FFmpegAudioStreamInfo AInfo = settings.ConvertToAvi ? await Task.Run(() => MediaInfo.GetFileInfo(settings.FilePath).AudioStream) : FInfo.AudioStream; settings.SourceWidth = FInfo.VideoStream.Width; settings.SourceHeight = FInfo.VideoStream.Height; if (settings.SourceHeight > 768) { settings.OutputHeight = settings.SourceHeight.Value; } settings.SourceAspectRatio = (float)(VInfo?.PixelAspectRatio ?? 1); // Fix last track of VCDs that is widescreen. if (settings.SourceHeight == 288 && settings.SourceWidth == 352 && settings.SourceAspectRatio == 1.485f) { settings.SourceAspectRatio = 1.092f; } settings.SourceFrameRate = VInfo?.FrameRate; settings.SourceAudioFormat = AInfo?.Format; settings.SourceVideoFormat = VInfo?.Format; bool IsTvRange = VInfo?.ColorRange != "pc"; if (!string.IsNullOrEmpty(VInfo?.ColorMatrix)) { settings.SourceColorMatrix = VInfo.ColorMatrix.EndsWith("601") ? (IsTvRange ? ColorMatrix.Rec601 : ColorMatrix.Pc601) : (IsTvRange ? ColorMatrix.Rec709 : ColorMatrix.Pc709); } else { settings.SourceColorMatrix = VInfo?.Height < 600 ? (IsTvRange ? ColorMatrix.Rec601 : ColorMatrix.Pc601) : (IsTvRange ? ColorMatrix.Rec709 : ColorMatrix.Pc709); } settings.SourceChromaPlacement = string.Compare(VInfo?.Format, "mpeg1video", true) == 0 ? ChromaPlacement.MPEG1 : ChromaPlacement.MPEG2; settings.DegrainPrefilter = VInfo?.Height < 600 ? DegrainPrefilters.SD : DegrainPrefilters.HD; settings.SourceVideoBitrate = (int)(new FileInfo(previewFile).Length / FInfo.FileDuration.TotalSeconds / 1024 * 8); settings.SourceAudioBitrate = AInfo?.Bitrate; if (!settings.HasAudioOptions) { settings.AudioQuality = AInfo?.Bitrate > 0 ? AInfo.Bitrate : 256; } if (settings.AudioQuality > 384) { settings.AudioQuality = 384; } settings.SourceBitDepth = 8; // VInfo.BitDepth; //settings.DenoiseD = 2; //settings.DenoiseA = settings.SourceHeight < 720 ? 2 : 1; settings.Position = FInfo.FileDuration.TotalSeconds / 2; settings.VideoAction = settings.SourceHeight >= 1080 ? VideoAction.x264 : VideoAction.x265; settings.EncodeQuality = settings.SourceHeight >= 1080 ? 23 : 22; settings.EncodePreset = settings.SourceHeight >= 1080 ? EncodePresets.veryslow : EncodePresets.medium; // Use Cache to open file when file is over 500MB settings.CalculateSize(); }
public string GetPreviewSourceFile(MediaEncoderSettings settings) { if (settings.ConvertToAvi) { return(PathManager.PreviewSourceFile); } else { return(settings.FilePath); } }
/// <summary> /// Saves the audio output of specified script into a WAV file. /// </summary> /// <param name="settings">An object containing the encoding settings.</param> /// <param name="destination">The WAV file to write.</param> /// <param name="options">The options that control the behaviors of the process.</param> public static void SaveAudioToWav(MediaEncoderSettings settings, string destination, ProcessStartOptions options) { string TempFile = settings.TempFile + ".avs"; AviSynthScriptBuilder Script = new AviSynthScriptBuilder(); if (settings.VideoAction != VideoAction.Copy) { // Read source script. Script.Script = File.ReadAllText(settings.ScriptFile); // Remote MT code. Script.RemoveMT(); Script.AppendLine("Trim(0,0)"); } else { // Read full video file. Script.AddPluginPath(); if (settings.ConvertToAvi || settings.InputFile.ToLower().EndsWith(".avi")) { Script.OpenAvi(settings.InputFile, !string.IsNullOrEmpty(settings.SourceAudioFormat)); } else { Script.OpenDirect(settings.InputFile, !string.IsNullOrEmpty(settings.SourceAudioFormat)); } Script.AppendLine("KillVideo()"); } Script.AppendLine(); // Add audio gain. if (settings.AudioGain.HasValue && settings.AudioGain != 0) { Script.AppendLine("AmplifydB({0})", settings.AudioGain.Value); } if (settings.ChangeAudioPitch) { // Change pitch to 432hz. Script.LoadPluginDll("TimeStretch.dll"); Script.AppendLine("ResampleAudio(48000)"); Script.AppendLine("TimeStretchPlugin(pitch = 100.0 * 0.98181819915771484)"); } // Add TWriteWAV. Script.AppendLine(); Script.LoadPluginDll("TWriteAVI.dll"); Script.AppendLine(@"TWriteWAV(""{0}"", true)", Script.GetAsciiPath(destination)); Script.AppendLine("ForceProcessWAV()"); // Write temp script. Script.WriteToFile(TempFile); // Execute. It aways returns an error but the file is generated. FFmpegProcess Worker = new FFmpegProcess(options); Worker.RunAvisynth(TempFile); File.Delete(TempFile); }
/// <summary> /// Automatically reloads jobs if the encoder was unexpectedly closed. /// </summary> public void AutoLoadJobs() { try { if (!Directory.Exists(PathManager.TempFilesPath)) { Directory.CreateDirectory(PathManager.TempFilesPath); } } catch { return; } var JobList = Directory.EnumerateFiles(PathManager.TempFilesPath, "Job*_Settings.xml"); int Index = 0; MediaEncoderSettings settings; //List<Task> TaskList = new List<Task>(); // Get list of interrupted jobs. foreach (string item in JobList) { // Load settings file. settings = null; try { settings = MediaEncoderSettings.Load(item); } catch { } // Resume job. if (int.TryParse(Path.GetFileName(item).Replace("Job", "").Replace("_Settings.xml", ""), out Index)) { if (Index > JobIndex) { JobIndex = Index; } if (settings != null && File.Exists(settings.InputFile) && File.Exists(settings.ScriptFile) && File.Exists(settings.SettingsFile) && File.Exists(settings.FilePath)) { ProcessingQueue.Add(settings); //TaskList.Add(StartJobAsync(settings)); } else { // Resume job failed, delete files. PathManager.DeleteJobFiles(settings.JobIndex); } } } }
/// <summary> /// Encodes specified audio file according to settings. The script file must already be written. /// </summary> /// <param name="settings">An object containing the encoding settings.</param> /// <returns>The endoding completion status..</returns> public static CompletionStatus EncodeAudio(MediaEncoderSettings settings) { CompletionStatus Result = CompletionStatus.Success; string WavFile = PathManager.GetAudioFile(settings.JobIndex, AudioActions.Wav); ProcessStartOptions Options = new ProcessStartOptions(settings.JobIndex, "Exporting Audio", false).TrackProcess(settings); if (!File.Exists(WavFile)) { EncoderBusiness.SaveAudioToWav(settings, WavFile, Options); if (settings.CompletionStatus == CompletionStatus.Cancelled) { File.Delete(WavFile); return(CompletionStatus.Cancelled); } if (!File.Exists(WavFile)) { settings.Cancel(); return(CompletionStatus.Error); } } string DestFile = PathManager.GetAudioFile(settings.JobIndex, settings.AudioAction); if (!File.Exists(DestFile)) { Options.Title = "Encoding Audio"; if (settings.AudioAction == AudioActions.Opus) { string Args = string.Format(@"--bitrate {0} ""{1}"" ""{2}""", settings.AudioQuality, WavFile, DestFile); FFmpegProcess Worker = new FFmpegProcess(Options); Result = Worker.Run("Encoder\\opusenc.exe", Args); } else if (settings.AudioAction == AudioActions.Aac || settings.AudioAction == AudioActions.Flac) { Result = FFmpeg.MediaEncoder.Encode(WavFile, null, settings.AudioAction == AudioActions.Flac ? "flac" : "aac", string.Format("-b:a {0}k", settings.AudioQuality), DestFile, Options); } } if (Result != CompletionStatus.Success || !File.Exists(DestFile)) { File.Delete(DestFile); settings.Cancel(); } return(Result); }
public bool PauseEncoding() { if (IsEncoding) { IsEncoding = false; MediaEncoderSettings Job = ProcessingQueue.FirstOrDefault(); if (Job != null) { Job.CompletionStatus = CompletionStatus.Cancelled; FFmpegConfig.UserInterfaceManager.Stop(Job.JobIndex); return(true); } } return(false); }
/// <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); } }
public static MediaEncoderSettings Load(string filePath) { using (var stream = File.OpenRead(filePath)) { var serializer = new XmlSerializer(typeof(MediaEncoderSettings)); MediaEncoderSettings Result = serializer.Deserialize(stream) as MediaEncoderSettings; if (Result.DeshakerSettings == null) { Result.DeshakerSettings = new MediaEncoderDeshakerSettings(); } // Serialization changes new lines; restore them. if (Result.CustomScript != null) { Result.CustomScript = Result.CustomScript.Replace("\n", "\r\n"); } return(Result); } }
private CompletionStatus GenerateDeshakerLogSegment(MediaEncoderSettings settings, string inputFile, int segment, long jobStart, long frameStart, long frameEnd, ProcessStartOptions jobOptions) { // Write Deshaker Pass 1 script to file. string Script = MediaEncoderScript.GenerateDeshakerScript(settings, inputFile, segment, frameStart, frameEnd); File.WriteAllText(settings.DeshakerScript, Script); // Run pass. jobOptions.IsMainTask = true; jobOptions.Title = "Running Deshaker Prescan"; jobOptions.ResumePos = frameStart - jobStart; CompletionStatus Result = FFmpeg.MediaEncoder.ConvertToAvi(settings.DeshakerScript, settings.DeshakerTempOut, false, jobOptions); File.Delete(settings.DeshakerScript); File.Delete(settings.DeshakerTempOut); return(Result); }
public void GenerateScript(MediaEncoderSettings settings, bool preview, bool multiThreaded) { AviSynthScriptBuilder Script = new AviSynthScriptBuilder(settings.CustomScript); if (Script.IsEmpty) { Script = MediaEncoderScript.GenerateVideoScript(settings, GetPreviewSourceFile(settings), preview, multiThreaded); } else if (preview) { Script.RemoveMT(); Script.AppendLine(@"ConvertToRGB32(matrix=""Rec709"")"); } else if (!multiThreaded) { Script.RemoveMT(); } Script.WriteToFile(PathManager.PreviewScriptFile); settings.Save(PathManager.PreviewSettingsFile); }
/// <summary> /// Auto-load Preview file if encoded was unexpectedly closed. /// </summary> /// <returns>The previous preview encoding settings.</returns> public async Task <MediaEncoderSettings> AutoLoadPreviewFileAsync() { if (File.Exists(PathManager.PreviewSettingsFile)) { MediaEncoderSettings settings = MediaEncoderSettings.Load(PathManager.PreviewSettingsFile); if (!File.Exists(PathManager.PreviewSourceFile) && File.Exists(settings.FilePath)) { double?SourceFps = settings.SourceFrameRate; // Keep FPS in case it cannot be read from the file. await PreparePreviewFile(settings, false, false); if (!settings.SourceFrameRate.HasValue) { settings.SourceFrameRate = SourceFps; } } settings.JobIndex = -1; return(settings); } return(null); }
private EncodingCompletedEventArgs FinalizeEncoding(MediaEncoderSettings settings, DateTime?startTime) { settings.ResumePos = -1; string VideoFile = settings.VideoAction == VideoAction.Discard ? null : settings.VideoAction == VideoAction.Copy ? settings.FilePath : settings.OutputFile; if (!File.Exists(settings.OutputFile) && settings.VideoAction != VideoAction.Copy) { // Merge segments. settings.CompletionStatus = MergeSegments(settings, VideoFile); if (settings.CompletionStatus != CompletionStatus.Success) { return(null); } } if (!File.Exists(settings.FinalFile)) { // Muxe video with audio. string AudioFile = settings.AudioAction == AudioActions.Discard ? null : settings.AudioAction == AudioActions.Copy ? settings.FilePath : settings.AudioFile; if (AudioFile == null) { File.Move(VideoFile, settings.FinalFile); } else if (VideoFile == null) { File.Move(AudioFile, settings.FinalFile); } else { settings.CompletionStatus = MediaMuxer.Muxe(VideoFile, AudioFile, settings.FinalFile, new ProcessStartOptions(settings.JobIndex, "Muxing Audio and Video", false)); } if (settings.CompletionStatus != CompletionStatus.Success) { return(null); } } return(GetEncodingResults(settings, settings.FinalFile, startTime)); }
private static void ApplyInterFrame(AviSynthScriptBuilder Script, MediaEncoderSettings settings, int CPU) { Script.LoadPluginDll("FrameRateConverter.dll"); Script.LoadPluginAvsi("FrameRateConverter.avsi"); Script.LoadPluginDll("MaskTools2.dll"); Script.LoadPluginDll("MvTools2.dll"); string Prefilter = ""; if (!settings.Denoise) { Script.LoadPluginDll("RgTools.dll"); Prefilter = ", Prefilter=RemoveGrain(21)"; } if (settings.IncreaseFrameRateValue == FrameRateModeEnum.Double) { Script.AppendLine(@"FrameRateConverter(FrameDouble=true){0}", Prefilter); } else { int NewNum = 0; int NewDen = 0; if (settings.IncreaseFrameRateValue == FrameRateModeEnum.fps30) { NewNum = 30; // 30000; NewDen = 1; // 1001; } else if (settings.IncreaseFrameRateValue == FrameRateModeEnum.fps60) { NewNum = 60; // 60000; NewDen = 1; // 1001; } else if (settings.IncreaseFrameRateValue == FrameRateModeEnum.fps120) { NewNum = 120; // 120000; NewDen = 1; // 1001; } Script.AppendLine(@"FrameRateConverter(NewNum={0}, NewDen={1}, Preset=""{2}""{3})", NewNum, NewDen, settings.IncreaseFrameRatePreset, Prefilter); } }
private static void ApplyDeshaker(AviSynthScriptBuilder Script, MediaEncoderSettings settings, ref bool isLsb, ref bool isYV24, bool multiProcess) { settings.DeshakerSettings.LogFile = Script.GetAsciiPath(settings.DeshakerLog); settings.DeshakerSettings.Pass = 2; Script.LoadPluginDll(@"VDubFilter.dll"); Script.AppendLine(@"LoadVirtualDubPlugin(P+""deshaker.vdf"", ""deshaker"", preroll=0)"); Script.AppendLine(@"ConvertToRGB32(matrix=""Rec709"")"); if (multiProcess) { Script.AppendLine("### prefetch: 3, 3"); // Keep 3 frames before and after for temporal filters. Script.AppendLine("### ###"); // MP_Pipeline starts new process here } Script.AppendLine(@"deshaker(""{0}"")", settings.DeshakerSettings.ToString()); Script.AppendLine(@"ConvertToYV12(matrix=""Rec709"")"); if (multiProcess) { Script.AppendLine("### prefetch: {0}, {1}", settings.DeshakerSettings.FillBordersWithFutureFrames ? settings.DeshakerSettings.FillBordersWithFutureFramesCount : 0, settings.DeshakerSettings.FillBordersWithPreviousFrames ? settings.DeshakerSettings.FillBordersWithPreviousFramesCount : 0); Script.AppendLine("### ###"); // MP_Pipeline starts another here } isLsb = false; isYV24 = false; }
public CompletionStatus GenerateDeshakerLog(MediaEncoderSettings settings, string inputFile) { // Prepare Deshaker settings. settings.DeshakerSettings.Pass = 1; settings.DeshakerSettings.LogFile = settings.DeshakerTempLog; settings.DeshakerSettings.SourcePixelAspectRatio = settings.SourceAspectRatio.Value; // settings.DeshakerSettings.AppendToFile = true; File.Delete(settings.DeshakerLog); // Start UI. CompletionStatus Result = CompletionStatus.Success; object JobId = "Deshaker"; FFmpegConfig.UserInterfaceManager.Start(JobId, "Running Deshaker Prescan"); ProcessStartOptions JobOptions = new ProcessStartOptions(JobId, "Getting Frame Count", false); // Get frame count. settings.CalculateSize(); int FrameStart = (int)((settings.DeshakerSettings.PrescanStart ?? 0) * settings.SourceFrameRate.Value); int FrameEnd = (int)((settings.DeshakerSettings.PrescanEnd ?? 0) * settings.SourceFrameRate.Value); string Script = MediaEncoderScript.GenerateDeshakerScript(settings, inputFile, 0, FrameStart, FrameEnd); File.WriteAllText(settings.DeshakerScript, Script); JobOptions.FrameCount = AvisynthTools.GetFrameCount(settings.DeshakerScript, JobOptions); // Pad file start. using (StreamWriter sw = new StreamWriter(File.Open(settings.DeshakerLog, FileMode.Create), Encoding.ASCII)) { for (int i = 0; i < FrameStart; i++) { sw.WriteLine((i + 1).ToString().PadLeft(7) + " skipped # 0.00 0.00 "); } } // Run segments. var Segments = settings.DeshakerSettings.Segments; if (JobOptions.FrameCount > 0) { for (int i = 0; i < Segments.Count; i++) { // Get start position of next segment. long NextSegmentStart = i < Segments.Count - 1 ? Segments[i + 1].FrameStart : 0; if (NextSegmentStart == 0 || NextSegmentStart > FrameStart) // Enforce PrescanStart for preview { long SegmentStart = Segments[i].FrameStart; long SegmentEnd = NextSegmentStart > 0 ? NextSegmentStart - 1 : 0; // Enforce PrescanEnd for preview if ((FrameEnd > 0 && SegmentStart > FrameEnd) || (SegmentEnd > 0 && SegmentStart > SegmentEnd)) { break; } if ((FrameStart > 0 && FrameStart > SegmentStart) || SegmentStart == 0) { SegmentStart = FrameStart; } if ((FrameEnd > 0 && FrameEnd < SegmentEnd) || SegmentEnd == 0) { SegmentEnd = FrameEnd; } Result = GenerateDeshakerLogSegment(settings, inputFile, i, FrameStart, SegmentStart, SegmentEnd, JobOptions); if (Result != CompletionStatus.Success) { break; } // Merge log segment into log file and set right frame numbers. using (StreamWriter sw = new StreamWriter(File.Open(settings.DeshakerLog, FileMode.Append), Encoding.ASCII)) { using (StreamReader sr = new StreamReader(File.OpenRead(settings.DeshakerTempLog), Encoding.ASCII)) { string LogLine, LogNum, LogNumField, LineOut; long NewLineNum = SegmentStart; LogLine = sr.ReadLine(); while (LogLine != null) { if (LogLine.Length > 7) { LogNum = LogLine.Substring(0, 7).Trim(); if (LogNum.Length > 0) { LogNumField = LogNum[LogNum.Length - 1].ToString(); if (LogNumField != "A" && LogNumField != "B") // For interlaced videos { LogNumField = ""; } NewLineNum++; // Log file starts at 1, not 0. LineOut = (NewLineNum.ToString() + LogNumField).PadLeft(7) + LogLine.Substring(7, LogLine.Length - 8); sw.WriteLine(LineOut); } } LogLine = sr.ReadLine(); } } } File.Delete(settings.DeshakerTempLog); } } } else { Result = CompletionStatus.Error; } // End UI. FFmpegConfig.UserInterfaceManager.Stop(JobId); return(Result); }
public void GenerateCustomScript(MediaEncoderSettings settings) { settings.CustomScript = MediaEncoderScript.GenerateVideoScript(settings, GetPreviewSourceFile(settings), false, true).Script; }
/// <summary> /// Tracks the process that will be run into a MediaEncoderSettings object. /// </summary> /// <param name="options">The options object used to track process status.</param> /// <param name="settings">The settings object that will store the process reference.</param> /// <returns>The options object.</returns> public static ProcessStartOptions TrackProcess(this ProcessStartOptions options, MediaEncoderSettings settings) { options.Started += (sender, e) => { if (settings.Processes == null) { settings.Processes = new List <FFmpegProcess>(); } settings.Processes.Add(e.Process); e.Process.Completed += (sender2, e2) => { settings.CompletionStatus = e2.Status; settings.Processes.Remove(e.Process); }; }; return(options); }
/// <summary> /// Encodes specified video file according to settings. The script file must already be written. /// </summary> /// <param name="settings">An object containing the encoding settings.</param> /// <param name="frameCount">The amount of frames to process.</param> /// <param name="totalFrameCount">If encoding in various segments, the total amount of frames in the script.</param> /// <returns>The endoding completion status..</returns> public static CompletionStatus EncodeVideo(MediaEncoderSettings settings, long frameCount, long totalFrameCount) { CompletionStatus Result = CompletionStatus.None; File.Delete(settings.OutputFile); string Codec = "", Args = ""; if (settings.VideoAction == VideoAction.x264) { Codec = "libx264"; } else if (settings.VideoAction == VideoAction.x265) { Codec = "libx265"; Args = string.Format("-preset {0} -crf {1}", settings.EncodePreset, settings.EncodeQuality); } else if (settings.VideoAction == VideoAction.Avi) { Codec = "huffyuv"; } else if (settings.VideoAction == VideoAction.AviUtVideo) { Codec = "utvideo"; } else if (settings.VideoAction == VideoAction.xvid) { Codec = "xvid"; } else if (settings.VideoAction == VideoAction.x264_10bit) { Args = string.Format("--preset {0} --crf {1}", settings.EncodePreset, settings.EncodeQuality); } if (settings.ParallelProcessing > 1) { Args += settings.VideoAction == VideoAction.x264_10bit ? " --threads 4" : " -threads 4"; } ProcessStartOptions Options = new ProcessStartOptions(settings.JobIndex, "Processing Video", true).TrackProcess(settings); // Options.FrameCount = AvisynthTools.GetFrameCount(settings.ScriptFile, new ProcessStartOptions(settings.JobIndex, "Getting Frame Count", false).TrackProcess(settings)); Options.FrameCount = frameCount; Options.ResumePos = settings.ResumePos; Options.TotalFrameCount = totalFrameCount; if (settings.CompletionStatus == CompletionStatus.Success) { string JobScript = settings.OutputScriptFile; File.Delete(JobScript); File.Copy(settings.ScriptFile, JobScript); EditStartPosition(JobScript, settings.ResumePos, settings.ResumePos + frameCount - 1); if (settings.VideoAction == VideoAction.x264_10bit) { Result = EncodeX264_10bit(JobScript, Args, settings.OutputFile, Options); } else { Result = FFmpeg.MediaEncoder.Encode(JobScript, Codec, null, Args, settings.OutputFile, Options); } File.Delete(JobScript); } else { Result = settings.CompletionStatus; } return(Result); }
/// <summary> /// Analyzes output files to determine what work is done and what needs to be done. /// </summary> /// <param name="settings">The media encoder settings of the job to scan.</param> /// <returns>Whether job needs to execute.</returns> public void Analyze(MediaEncoderSettings settings) { int Threads = settings.ParallelProcessing; if (Threads == 0) { Threads = 1; } this.settings = settings; SegDone = new List <SegmentInfo>(); SegLeft = new List <SegmentInfo>(); // Get script total frame count, and run in background until all other files are scanned. EncoderBusiness.EditStartPosition(settings.ScriptFile, 0, 0); ProcessStartOptions Options = new ProcessStartOptions(settings.JobIndex, "Analyzing Segments...", false).TrackProcess(settings); Task <long> TaskCount = Task.Run(() => AvisynthTools.GetFrameCount(settings.ScriptFile, Options)); // Get list of output files in folder. The number after "_Output_" is the frame position of that segment. string FileName = string.Format("Job{0}_Output_", settings.JobIndex); string FileNameExt = string.Format(".{0}", settings.Container); string[] SegFiles = Directory.GetFiles(PathManager.TempFilesPath, FileName + "*" + FileNameExt); // Scan each segment file and discard files smaller than 10kb or of less than 10 frames. // Create a list of tasks to run them all in parallel. List <Task <KeyValuePair <string, long> > > TaskList = new List <Task <KeyValuePair <string, long> > >(); foreach (string seg in SegFiles) { if (settings.CompletionStatus == CompletionStatus.Cancelled) { break; } // Discard empty files. if (new FileInfo(seg).Length > 0) { // string SegmentLocal = seg; TaskList.Add(Task.Run(() => { return(new KeyValuePair <string, long>(seg, MediaInfo.GetFrameCount(seg, null))); })); } else { File.Delete(seg); } } // Run all segment length queries simultaneously and analyze results to fill SegDone. Task.WaitAll(TaskList.ToArray()); string SegFile; long SegStart; bool SegAdded; int Pos; string SegText; if (settings.CompletionStatus != CompletionStatus.Cancelled) { foreach (var item in TaskList) { SegFile = item.Result.Key; SegStart = 0; SegAdded = false; if (item.Result.Value >= 1) // Segment must contain at least one valid frame. { Pos = SegFile.IndexOf(FileName); if (Pos > -1) { Pos += FileName.Length; SegText = SegFile.Substring(Pos, SegFile.Length - Pos - FileNameExt.Length); if (long.TryParse(SegText, out SegStart)) { SegDone.Add(new SegmentInfo(SegStart, SegStart + item.Result.Value)); SegAdded = true; } } } if (!SegAdded) { File.Delete(SegFile); } } // Order can be random, must sort. SegDone = SegDone.OrderBy(s => s.Start).ToList(); } // Get script total frames and calculate the work left. TaskCount.Wait(); if (settings.CompletionStatus == CompletionStatus.Cancelled) { SegDone.Clear(); } else { // Create list of segments left. TotalFrames = TaskCount.Result; long SegPos = 0; foreach (SegmentInfo segd in SegDone) { if (segd.Start > SegPos) { SegLeft.Add(new SegmentInfo(SegPos, segd.Start - 1)); } SegPos = segd.End + 1; } if (SegPos < TotalFrames) { SegLeft.Add(new SegmentInfo(SegPos, TotalFrames - 1)); } if (settings.ParallelProcessing > 1) { // Divide in segments int Instances = settings.ParallelProcessing; int SmallSegSize = SegSize / Instances; int StartSegSize = ((Instances - 1) * Instances / 2) * SmallSegSize; SegmentInfo Seg; int SegCount = 0; long IdealSeg, AvailSeg; // Work on copy because original list will be modified. List <SegmentInfo> SegLeftCopy = SegLeft.ToList(); SegLeft.Clear(); for (int i = 0; i < SegLeftCopy.Count(); i++) { Seg = SegLeftCopy[i]; AvailSeg = Seg.Length; while (AvailSeg > 0) { // Start with smaller segments. IdealSeg = SegCount < Instances ? ++SegCount * SmallSegSize : SegSize; // Split segment only if it is larger than threshold if (AvailSeg > IdealSeg + SmallSegSize) { SegLeft.Add(new SegmentInfo(Seg.Start, Seg.Start + IdealSeg - 1)); Seg.Start += IdealSeg; AvailSeg -= IdealSeg; } else { SegLeft.Add(new SegmentInfo(Seg.Start, Seg.End)); AvailSeg = 0; } } } // If we're still missing segments (for short clips), split using other method. // Create segments to reach the desired amount of threads. int SegMissing = SegLeft.Count > 0 ? Threads - SegLeft.Count() : 0; for (int i = 0; i < SegMissing; i++) { // Find largest segment. int SegMaxIndex = 0; long SegMax = SegLeft[0].Length; for (int j = 1; j < SegLeft.Count(); j++) { if (SegLeft[j].Length > SegMax) { SegMaxIndex = j; SegMax = SegLeft[j].Length; } } // Split largest segment in half. Seg = SegLeft[SegMaxIndex]; if (Seg.Length > 80) // Only split if segment has at least 80 frames (creating segments of 40). { long SegSep = Seg.Start + (Seg.Length - 1) / 2; SegLeft[SegMaxIndex] = new SegmentInfo(Seg.Start, SegSep); SegLeft.Insert(SegMaxIndex + 1, new SegmentInfo(SegSep + 1, Seg.End)); } } } } }
public static AviSynthScriptBuilder GenerateVideoScript(MediaEncoderSettings settings, string inputFile, bool preview, bool multiThreaded) { // int CPU = multiThreaded ? Environment.ProcessorCount : 1; int CPU = multiThreaded ? settings.Threads : 1; if (CPU <= 1) { multiThreaded = false; } bool RunMultiProcesses = false; if (settings.Deshaker && multiThreaded) { RunMultiProcesses = true; // Run through MP_Pipeline multiThreaded = false; // Deshaker doesn't work with MT CPU = 1; } //if (settings.Threads > 0) { // multiThreaded = false; // CPU = 1; //} settings.CalculateSize(); // Calculate encoding and final frame rates. double ChangeSpeedValue = (settings.ChangeSpeed && settings.CanAlterAudio) ? (double)settings.ChangeSpeedValue / 100 : 1; double FrameRateBefore = settings.SourceFrameRate.Value * ChangeSpeedValue; // Generate video script. AviSynthScriptBuilder Script = new AviSynthScriptBuilder(); bool IsYV24 = false; Script.AppendLine(); // After clean-up, this adds a line after LoadPlugin commands. Script.AddPluginPath(); if (multiThreaded) { Script.LoadPluginAvsi("AviSynthMT.avsi"); } if (settings.ConvertToAvi || inputFile.ToLower().EndsWith(".avi")) { Script.OpenAvi(inputFile, !string.IsNullOrEmpty(settings.SourceAudioFormat)); } else { Script.OpenDirect(inputFile, !string.IsNullOrEmpty(settings.SourceAudioFormat)); } if (FrameRateBefore != settings.SourceFrameRate) { Script.AppendLine(CultureInfo.InvariantCulture, "AssumeFPS({0}, true)", FrameRateBefore); } if (settings.Trim && settings.CanAlterAudio) { Script.AppendLine("Trim({0}, {1})", (settings.TrimStart ?? 0) > 0 ? (int)(settings.TrimStart.Value * settings.SourceFrameRate.Value) : 0, (settings.TrimEnd ?? 0) > 0 && !preview ? (int)(settings.TrimEnd.Value * settings.SourceFrameRate.Value) : 0); } if (settings.Denoise) { //Script.LoadPluginDll("MvTools2.dll"); //Script.LoadPluginDll("MaskTools2.dll"); //Script.LoadPluginDll("FFT3DFilter.dll"); //Script.LoadPluginDll("ModPlus.dll"); //Script.LoadPluginDll("RgTools.dll"); //Script.LoadPluginDll("DCTFilter.dll"); //Script.LoadPluginAvsi("mClean.avsi"); //Script.AppendLine("mClean(rn={0}{1})", 10, IsHD ? ", outbits=16" : ""); Script.AppendLine(@"MCTemporalDenoise(settings=""{0}""{1})", "medium", settings.Deblock ? ", deblock=true" : ""); } else if (settings.Deblock) { Script.LoadPluginDll("MaskTools2.dll"); Script.LoadPluginDll("DCTFilter.dll"); Script.LoadPluginDll("deblock.dll"); Script.LoadPluginAvsi("Deblock_QED.avsi"); Script.AppendLine("Deblock_QED()"); } if (settings.CropSource.HasValue) { Script.AppendLine("Crop({0}, {1}, -{2}, -{3})", settings.CropSource.Left, settings.CropSource.Top, settings.CropSource.Right, settings.CropSource.Bottom); } // If possible, KNLMeans will output 16-bit frames before upscaling. bool IsHD = (settings.Dering || settings.Deblock || settings.Degrain || settings.SourceColorMatrix != ColorMatrix.Rec709 || (settings.FrameDouble > 0 && (settings.SuperRes || settings.UpscaleMethod == UpscaleMethods.SuperXbr))); bool IsColorMatrixHD = IsHD; bool IsChromaFixed = settings.SourceChromaPlacement == ChromaPlacement.MPEG2; if (IsHD) { //if (!settings.Denoise) Script.AppendLine("ConvertBits(16)"); if (settings.SourceColorMatrix != ColorMatrix.Rec709) { Script.AppendLine("[ColorMatrixShader]"); // Placeholder that will be replaced after generating the rest. } } if (settings.FixDoubleFrames) { Script.LoadPluginDll("FrameRateConverter.dll"); Script.LoadPluginAvsi("FrameRateConverter.avsi"); Script.LoadPluginDll("MaskTools2.dll"); Script.LoadPluginDll("MvTools2.dll"); Script.AppendLine("InterpolateDoubles(.1)"); } if (settings.Dering) { Script.LoadPluginAvsi("HQDeringmod.avsi"); Script.LoadPluginDll("MedianBlur2.dll"); Script.LoadPluginDll("dfttest.dll"); Script.LoadPluginDll("RgTools.dll"); Script.LoadPluginDll("SmoothAdjust.dll"); Script.AppendLine("HQDeringmod({0})", IsHD ? "lsb_in=true, lsb=true" : ""); } if (settings.Degrain) { ApplyDegrain(Script, settings, ref IsHD, ref IsYV24); } if (settings.Deshaker) { ApplyDeshaker(Script, settings, ref IsHD, ref IsYV24, RunMultiProcesses); } if (settings.FrameDouble > 0) { Script.AppendLine(@"ConvertToYUV444({0})", !IsChromaFixed ? string.Format(@"ChromaInPlacement=""{0}""", settings.SourceChromaPlacement.ToString()) : ""); IsYV24 = true; IsChromaFixed = true; } string FinalSize = string.Format("fWidth={0}, fHeight={1}", settings.OutputWidth + settings.CropAfter.Left + settings.CropAfter.Right, settings.OutputHeight + settings.CropAfter.Top + settings.CropAfter.Bottom); string FinalResize = FinalSize; if (settings.DownscaleMethod == DownscaleMethods.SSim) { FinalResize += string.Format(@", fKernel=""SSim"", fB={0}{1}", settings.SSimStrength / 100, settings.SSimSoft ? ", fC=1" : ""); } else // DownscaleMethod == Bicubic { FinalResize += string.Format(@", fKernel=""Bicubic"", fB=0, fC=.75"); } if (settings.FrameDouble > 0) { if (settings.UpscaleMethod == UpscaleMethods.NNedi3) { bool IsLastDouble = false; DitherPost(Script, ref IsHD, ref IsYV24, true); Script.LoadPluginDll("nnedi3.dll"); Script.LoadPluginAvsi("edi_rpow2.avsi"); Script.LoadPluginAvsi("ResizeX.avsi"); for (int i = 0; i < settings.FrameDouble; i++) { IsLastDouble = i == settings.FrameDouble - 1; string DoubleScript = string.Format("edi_rpow2(2, nns=4, {0}, Threads=2)", IsLastDouble && settings.DownscaleMethod == DownscaleMethods.Bicubic ? @"cshift=""Bicubic"", a1=0, a2=.75, " + FinalSize : @"cshift=""Spline16Resize"""); if (settings.SuperRes) { Script.AppendLine(CultureInfo.InvariantCulture, @"SuperRes({0}, {1}, {2}, """"""{3}""""""{4}{5}{6})", settings.SuperRes3Passes ? (i == 0 && settings.FrameDouble > 1 ? 5 : 3) : 2, settings.SuperResStrength / 100f, settings.SuperResSoftness / 100f, DoubleScript, i == 0 ? GetMatrixIn(settings) : "", IsLastDouble && settings.DownscaleMethod == DownscaleMethods.SSim ? ", " + FinalResize : "", multiThreaded ? "" : ", Engines=1"); } else { Script.AppendLine(DoubleScript); if (IsLastDouble && settings.DownscaleMethod == DownscaleMethods.SSim) { Script.AppendLine("ResizeShader({0}{1})", FinalResize.Replace("fWidth=", "").Replace("fHeight=", "").Replace(" f", " "), // Use same string as FinalResize but remove 'f' parameter sufix. GetMatrixIn(settings)); } } } } else // UpscaleMethod == SuperXBR { if (settings.SuperRes) { Script.AppendLine(CultureInfo.InvariantCulture, @"SuperResXBR({0}, {1}, {2}{3}, XbrStr={4}, XbrSharp={5}{6}, {7}, FormatOut=""YV12""{8})", settings.SuperRes3Passes ? (settings.IncreaseFrameRate ? 5 : 3) : 2, settings.SuperResStrength / 100f, settings.SuperResSoftness / 100f, settings.FrameDouble > 1 ? ", Factor=" + (1 << settings.FrameDouble).ToString() : "", settings.SuperXbrStrength / 10f, settings.SuperXbrSharpness / 10f, GetMatrixIn(settings), FinalResize, multiThreaded ? "" : ", Engines=1"); } else { Script.AppendLine(@"SuperXBR(Str={0}, Sharp={1}{2}{3}, {4}, FormatOut=""YV12""{5})", settings.SuperXbrStrength / 10f, settings.SuperXbrSharpness / 10f, settings.FrameDouble > 1 ? ", Factor=" + (1 << settings.FrameDouble).ToString() : "", GetMatrixIn(settings), FinalResize, multiThreaded ? "" : ", Engines=1"); } IsHD = false; IsYV24 = false; } } if (settings.DownscaleMethod != DownscaleMethods.SSim) { DitherPost(Script, ref IsHD, ref IsYV24, settings.IncreaseFrameRate); } string[] ShaderCommands = new string[] { "SuperRes", "SuperXBR", "SuperResXBR", "ResizeShader" }; if (settings.CropAfter.HasValue || settings.FrameDouble == 0) { if (settings.CropAfter.HasValue || settings.SourceAspectRatio != 1 || settings.OutputWidth != settings.SourceWidth || settings.OutputHeight != settings.OutputHeight) { if (settings.DownscaleMethod == DownscaleMethods.SSim && settings.FrameDouble == 0) { Script.AppendLine("ResizeShader({0}{1})", FinalResize.Replace("fWidth=", "").Replace("fHeight=", "").Replace(" f", " "), // Use same string as FinalResize but remove 'f' parameter sufix. GetMatrixIn(settings)); } DitherPost(Script, ref IsHD, ref IsYV24, false); if (settings.CropAfter.HasValue || settings.DownscaleMethod == DownscaleMethods.Bicubic) { Script.LoadPluginAvsi("ResizeX.avsi"); bool ApplyResize = settings.FrameDouble == 0 || !Script.ContainsAny(ShaderCommands); string CropFormat = "ResizeX"; CropFormat += settings.CropAfter.HasValue ? "({0}, {1}, {2}, {3}, -{4}, -{5}{6})" : "({0}, {1}{6})"; Script.AppendLine(CropFormat, settings.OutputWidth, settings.OutputHeight, settings.CropAfter.Left, settings.CropAfter.Top, settings.CropAfter.Right, settings.CropAfter.Bottom, ApplyResize ? @", kernel=""Bicubic"", a1=0, a2=.75" : ""); } } } // Use ColorMatrixShader only if it wasn't already applied through any other shader. if (Script.ContainsAny(ShaderCommands)) { // ColorMatrix was applied though another shader. Script.Replace("[ColorMatrixShader]" + Environment.NewLine, ""); Script.LoadPluginDll("Shader.dll"); Script.LoadPluginAvsi("Shader.avsi"); } else { string ColorMatrixScript = ""; // Apply color matrix if (settings.SourceColorMatrix == ColorMatrix.Pc709) { Script.LoadPluginDll("SmoothAdjust.dll"); ColorMatrixScript = string.Format(@"ColorYUV(levels=""PC->TV"""); } else if (settings.SourceColorMatrix != ColorMatrix.Rec709) { Script.LoadPluginDll("Shader.dll"); Script.LoadPluginAvsi("Shader.avsi"); ColorMatrixScript = string.Format(@"ResizeShader(Kernel=""ColorMatrix""{0})", GetMatrixIn(settings)); } Script.Replace("[ColorMatrixShader]" + Environment.NewLine, ColorMatrixScript + Environment.NewLine); } if (settings.IncreaseFrameRate) { if (IsYV24) { Script.AppendLine(@"ConvertToYUV420()"); IsYV24 = false; } DitherPost(Script, ref IsHD, ref IsYV24, true); ApplyInterFrame(Script, settings, CPU); } if (preview) { if (settings.Crop) { Script.AppendLine("AddBorders(2, 2, 2, 2, $FFFFFF)"); } Script.AppendLine(@"ConvertToRGB32(matrix=""Rec709"")"); } if (multiThreaded) { Script.AppendLine("Prefetch({0})", CPU); } if (RunMultiProcesses) { Script.ConvertToMultiProcesses(settings.SourceFrameRate.Value); } else { Script.Cleanup(); } return(Script); }
/// <summary> /// Generates a script for Deshaker prescan. This is a much simplified version of the full script that will execute faster. /// </summary> public static string GenerateDeshakerScript(MediaEncoderSettings settings, string inputFile, int segment, long frameStart, long frameEnd) { bool MultiProcesses = false; // Calculate encoding and final frame rates. double ChangeSpeedValue = settings.ChangeSpeed ? (double)settings.ChangeSpeedValue / 100 : 1; double FrameRateBefore = settings.SourceFrameRate.Value * ChangeSpeedValue; // Generate video script. AviSynthScriptBuilder Script = new AviSynthScriptBuilder(); Script.AddPluginPath(); Script.LoadPluginDll("VDubFilter.dll"); Script.AppendLine(@"LoadVirtualDubPlugin(P+""deshaker.vdf"", ""deshaker"", preroll=0)"); if (settings.ConvertToAvi || inputFile.ToLower().EndsWith(".avi")) { Script.OpenAvi(inputFile, false); } else { Script.OpenDirect(inputFile, false); } if (FrameRateBefore != settings.SourceFrameRate) { Script.AppendLine(CultureInfo.InvariantCulture, "AssumeFPS({0}, true)", FrameRateBefore); } if (settings.Trim) { Script.AppendLine("Trim({0}, {1})", (settings.TrimStart ?? 0) > 0 ? (int)(settings.TrimStart.Value * settings.SourceFrameRate.Value) : 0, (settings.TrimEnd ?? 0) > 0 ? (int)(settings.TrimEnd.Value * settings.SourceFrameRate.Value) : 0); } if (settings.CropSource.HasValue) { Script.AppendLine("Crop({0}, {1}, -{2}, -{3})", settings.CropSource.Left, settings.CropSource.Top, settings.CropSource.Right, settings.CropSource.Bottom); } // Generate script for a segment. if (frameStart > 0 || frameEnd > 0) { Script.AppendLine("Trim({0}, {1})", frameStart, frameEnd); } // Resize to the dimention deshaker will run at. if (settings.FrameDouble == 1) { int FinalWidth = settings.OutputWidth.Value + settings.CropAfter.Left + settings.CropAfter.Right; int FinalHeight = settings.OutputHeight + settings.CropAfter.Top + settings.CropAfter.Bottom; Script.AppendLine("BicubicResize({0}, {1})", FinalWidth, FinalHeight); } else if (settings.FrameDouble > 1) { Script.AppendLine("BicubicResize(Width*2, Height*2)"); } // Deshaker requires RGB input. Script.AppendLine(@"ConvertToRGB32(matrix=""{0}"")", settings.SourceColorMatrix == ColorMatrix.Rec601 ? "Rec601" : settings.SourceColorMatrix == ColorMatrix.Pc709 ? "Pc709" : settings.SourceColorMatrix == ColorMatrix.Pc601 ? "Pc601" : "Rec709"); // Generate log file Script.AppendLine(@"deshaker(""{0}"")", settings.DeshakerSettings.ToString(segment)); // FFMPEG will generate an AVI file; make it 8x8 to minimize processing. Script.AppendLine("ConvertToYV12()"); Script.AppendLine("PointResize(64,64)"); if (MultiProcesses) { Script.AppendLine("### prefetch: 0, 0"); Script.AppendLine("### branch: 2"); Script.AppendLine("### ###"); Script.ConvertToMultiProcesses(settings.SourceFrameRate.Value); } return(Script.Script); }
public async Task PreparePreviewFile(MediaEncoderSettings settings, bool overwrite, bool calcAutoCrop) { if (string.IsNullOrEmpty(settings.FilePath)) { return; } if (overwrite) { File.Delete(PathManager.PreviewSourceFile); // Select default open method. if (settings.FilePath.ToLower().EndsWith(".avi")) { settings.ConvertToAvi = false; } else { FFmpegProcess FileInfo = await Task.Run(() => MediaInfo.GetFileInfo(settings.FilePath)); if (settings.ConvertToAvi && FileInfo?.VideoStream?.Height >= 720) { settings.ConvertToAvi = false; } } } bool AviFileReady = File.Exists(PathManager.PreviewSourceFile); if (!AviFileReady && settings.ConvertToAvi) { AviFileReady = await Task.Run(() => FFmpeg.MediaEncoder.ConvertToAvi(settings.FilePath, PathManager.PreviewSourceFile, true, new ProcessStartOptions(FFmpegDisplayMode.Interface, "Converting to AVI"))) == CompletionStatus.Success; } if (AviFileReady && settings.ConvertToAvi) { await GetMediaInfo(PathManager.PreviewSourceFile, settings); } else { settings.ConvertToAvi = false; await GetMediaInfo(settings.FilePath, settings); } // Auto-calculate crop settings. if (calcAutoCrop) { if (settings.CropLeft == 0 && settings.CropTop == 0 && settings.CropRight == 0 && settings.CropBottom == 0) { Rect AutoCrop = await Task.Run(() => EncoderBusiness.GetAutoCropRect(settings.FilePath, settings.SourceWidth ?? 0, settings.SourceHeight ?? 0, null)); if (settings.CropLeft == 0) { settings.CropLeft = AutoCrop.Left; } if (settings.CropTop == 0) { settings.CropTop = AutoCrop.Top; } if (settings.CropRight == 0) { settings.CropRight = AutoCrop.Right; } if (settings.CropBottom == 0) { settings.CropBottom = AutoCrop.Bottom; } } } }
public bool CustomScriptHasChanges(MediaEncoderSettings settings) { return(settings.CustomScript.Replace("\r\n", "\n") != MediaEncoderScript.GenerateVideoScript(settings, GetPreviewSourceFile(settings), false, true).Script.Replace("\r\n", "\n")); }