private void GetStreamInfo() { try { _currentTask.MediaInfo = GenHelper.GetMediaInfo(_currentTask.VideoStream.TempFile); } catch (TimeoutException ex) { Log.Error(ex); } finally { if (_currentTask.MediaInfo.Video.Count > 0) { _currentTask.VideoStream.Bitrate = _currentTask.MediaInfo.Video[0].BitRate; _currentTask.VideoStream.StreamSize = GenHelper.GetFileSize(_currentTask.VideoStream.TempFile); _currentTask.VideoStream.FrameCount = _currentTask.MediaInfo.Video[0].FrameCount; _currentTask.VideoStream.StreamId = _currentTask.MediaInfo.Video[0].ID; } } for (var i = 0; i < _currentTask.AudioStreams.Count; i++) { var aStream = _currentTask.AudioStreams[i]; aStream = AudioHelper.GetStreamInfo(aStream); _currentTask.AudioStreams[i] = aStream; } foreach (var sStream in _currentTask.SubtitleStreams) { sStream.StreamSize = GenHelper.GetFileSize(sStream.TempFile); } }
/// <summary> /// The ffmpeg process has exited. /// </summary> /// <param name="sender"> /// The sender. /// </param> /// <param name="e"> /// The EventArgs. /// </param> private void EncodeProcessExited(object sender, EventArgs e) { try { EncodeProcess.CancelErrorRead(); } catch (Exception exc) { Log.Error(exc); } _currentTask.ExitCode = EncodeProcess.ExitCode; Log.Info($"Exit Code: {_currentTask.ExitCode:0}"); if (_currentTask.ExitCode == 0) { if (_encProfile.EncodingMode == 0 || (_encProfile.EncodingMode == 1 && _encodingPass == 2)) { _currentTask.VideoStream.IsRawStream = false; _currentTask.VideoStream.Encoded = true; _currentTask.TempFiles.Add(_inputFile); _currentTask.VideoStream.TempFile = _outputFile; _currentTask.TempFiles.Add(_currentTask.AviSynthScript); try { _currentTask.MediaInfo = GenHelper.GetMediaInfo(_currentTask.VideoStream.TempFile); } catch (TimeoutException ex) { Log.Error(ex); } _currentTask.VideoStream = VideoHelper.GetStreamInfo(_currentTask.MediaInfo, _currentTask.VideoStream, _currentTask.EncodingProfile.OutFormat == OutputType.OutputBluRay); _currentTask.TempFiles.Add(_currentTask.FfIndexFile); } } _currentTask.CompletedStep = _currentTask.NextStep; IsEncoding = false; InvokeEncodeCompleted(new EncodeCompletedEventArgs(true, null, string.Empty)); }
public void GetFileInfo() { var mi = new MediaInfoContainer(); try { mi = GenHelper.GetMediaInfo(JobInfo.InputFile); } catch (TimeoutException ex) { Log.Error(ex); } JobInfo.MediaInfo = mi; const string strChapters = "Chapters"; const string strVideo = "Video"; const string strAudio = "Audio"; const string strSubtitles = "Subtitles"; var containerFormat = mi.General.Format; var duration = mi.General.DurationTime.ToString("H:mm:ss.fff"); var shortFileName = mi.General.FileName + "." + mi.General.FileExtension; var treeRoot = $"{shortFileName} / {containerFormat} / Length: {duration}"; var root = new StreamTreeNode { ID = _treeNodeID++, Name = treeRoot, Data = JobInfo.InputFile, IsChecked = true, IsExpanded = true, Children = new List <StreamTreeNode>() }; root.PropertyChanged += TreeNodePropertyChanged; _tree.Add(root); var chaptersStreamTree = CreateNode(root, strChapters, null); var videoStreamTree = CreateNode(root, strVideo, null); var audioStreamTree = CreateNode(root, strAudio, null); var subStreamTree = CreateNode(root, strSubtitles, null); if (mi.Chapters.Count > 0) { var chaptersTitle = $"{mi.Chapters.Count:0} {strChapters}"; CreateNode(chaptersStreamTree, chaptersTitle, mi.Chapters); } else { chaptersStreamTree.IsChecked = false; } var streamIndex = 0; foreach (var clip in mi.Video) { streamIndex++; var videoPid = clip.ID; var videoCodec = clip.FormatInfo; var videoCodecShort = clip.Format; var videoDesc = $"{clip.Width:0}x{clip.Height:0} {clip.ScanType} / Profile: {clip.FormatProfile} / {clip.FrameRate:0.000}fps"; var videoStreamTitle = $"{streamIndex:0}: {videoCodec} ({videoCodecShort}), {videoDesc}"; var vid = new VideoInfo(); if (JobInfo.Input == InputType.InputAvi) { vid.StreamId = 0; } else { vid.StreamId = videoPid == 0 ? streamIndex : videoPid; } vid.StreamKindID = clip.StreamKindID; vid.Fps = clip.FrameRate; vid.PicSize = clip.VideoSize; vid.Interlaced = clip.ScanType == "Interlaced"; vid.Format = clip.Format; vid.FormatProfile = clip.FormatProfile; vid.Height = clip.Height; vid.Width = clip.Width; vid.FrameCount = clip.FrameCount; vid.StreamSize = clip.StreamSize; vid.Length = mi.General.DurationTime.TimeOfDay.TotalSeconds; float.TryParse(clip.DisplayAspectRatio, NumberStyles.Number, _configService.CInfo, out vid.AspectRatio); vid.FrameRateEnumerator = clip.FrameRateEnumerator; vid.FrameRateDenominator = clip.FrameRateDenominator; vid.FrameMode = clip.FormatFrameMode; CreateNode(videoStreamTree, videoStreamTitle, vid); } videoStreamTree.IsChecked = videoStreamTree.Children.Count > 0; foreach (var audio in mi.Audio) { streamIndex++; var audioPid = audio.ID; var audioCodec = audio.FormatInfo; var audioCodecShort = audio.Format; var audioLangCode = audio.LanguageIso6392; var audioLanguage = audio.LanguageFull; var audioStreamKindID = audio.StreamKindID; var audioDesc = $"{audio.Channels:0} Channels ({audio.ChannelPositions}) / {audio.SamplingRate:0}Hz / "; audioDesc += $"{audio.BitDepth:0} bit / {audio.BitRate/1000:0} kbit/s"; var audioStreamTitle = $"{streamIndex:0}: {audioCodec} ({audioCodecShort}) / {audioLangCode} ({audioLanguage}) / {audioDesc}"; if (JobInfo.Input == InputType.InputAvi) { audioPid += 1; } else { audioPid = audioPid == 0 ? streamIndex : audioPid; } var aud = new AudioInfo { Id = audioPid, Format = audioCodecShort, FormatProfile = audio.FormatProfile, StreamId = streamIndex, LangCode = audioLangCode, OriginalId = audioPid, StreamKindId = audioStreamKindID, Delay = audio.Delay, Bitrate = audio.BitRate, SampleRate = audio.SamplingRate, ChannelCount = audio.Channels, BitDepth = audio.BitDepth, ShortLang = audio.LanguageIso6391, StreamSize = audio.StreamSize, Length = mi.General.DurationTime.TimeOfDay.TotalSeconds, IsHdStream = audio.CompressionMode == "Lossless" }; CreateNode(audioStreamTree, audioStreamTitle, aud); } audioStreamTree.IsChecked = audioStreamTree.Children.Count > 0; foreach (var sub in mi.Text) { streamIndex++; var subCodec = sub.CodecIDInfo; var subCodecShort = sub.Format; var subLangCode = sub.LanguageIso6392; var subLanguage = sub.LanguageFull; var subStreamKindID = sub.StreamKindID; var subStreamTitle = $"{streamIndex:0}: {subCodec} ({subCodecShort}) / {subLangCode} ({subLanguage})"; var subInfo = new SubtitleInfo { Id = sub.ID, StreamId = streamIndex, LangCode = subLangCode, Format = subCodecShort, StreamKindId = subStreamKindID, Delay = sub.Delay, StreamSize = sub.StreamSize }; CreateNode(subStreamTree, subStreamTitle, subInfo); } foreach (var sub in mi.Image) { streamIndex++; var subCodec = sub.CodecIDInfo; var subCodecShort = sub.Format; var subLangCode = sub.LanguageIso6392; var subLanguage = sub.LanguageFull; var subStreamKindID = sub.StreamKindID; var subStreamTitle = $"{streamIndex:0}: {subCodec} ({subCodecShort}) / {subLangCode} ({subLanguage})"; var subInfo = new SubtitleInfo { Id = sub.ID, StreamId = streamIndex, LangCode = subLangCode, Format = subCodecShort, StreamKindId = subStreamKindID, Delay = 0, StreamSize = sub.StreamSize }; CreateNode(subStreamTree, subStreamTitle, subInfo); } subStreamTree.IsChecked = subStreamTree.Children.Count > 0; NotifyOfPropertyChange(() => Tree); }
/// <summary> /// Execute a ffmpeg demux process. /// This should only be called from the UI thread. /// </summary> /// <param name="encodeQueueTask"> /// The encodeQueueTask. /// </param> public override void Start(EncodeInfo encodeQueueTask) { try { if (IsEncoding) { encodeQueueTask.ExitCode = -1; throw new Exception("ffmpeg is already running"); } IsEncoding = true; _currentTask = encodeQueueTask; var use64BitEncoder = _appConfig.Use64BitEncoders && _appConfig.Ffmpeg64Installed && Environment.Is64BitOperatingSystem; if (_currentTask.Input == InputType.InputDvd) { _inputFile = _currentTask.DumpOutput; } else { _inputFile = string.IsNullOrEmpty(_currentTask.TempInput) ? _currentTask.InputFile : _currentTask.TempInput; } _currentTask.VideoStream.TempFile = _inputFile; try { _currentTask.MediaInfo = GenHelper.GetMediaInfo(_inputFile); if (_currentTask.Input == InputType.InputDvd) { _currentTask.VideoStream = VideoHelper.GetStreamInfo(_currentTask.MediaInfo, _currentTask.VideoStream, false); } } catch (Exception ex) { Log.Error(ex); } var query = GenerateCommandLine(); var ffmpegCliPath = Path.Combine(_appConfig.ToolsPath, use64BitEncoder ? Executable64 : Executable); var cliStart = new ProcessStartInfo(ffmpegCliPath, query) { WorkingDirectory = _appConfig.DemuxLocation, CreateNoWindow = true, UseShellExecute = false, RedirectStandardError = true }; DemuxProcess = new Process { StartInfo = cliStart }; Log.Info($"start parameter: ffmpeg {query}"); DemuxProcess.Start(); _startTime = DateTime.Now; DemuxProcess.ErrorDataReceived += DemuxDataReceived; DemuxProcess.BeginErrorReadLine(); _demuxerProcessId = DemuxProcess.Id; // Set the encoder process exit trigger if (_demuxerProcessId != -1) { DemuxProcess.EnableRaisingEvents = true; DemuxProcess.Exited += DemuxProcessExited; } DemuxProcess.PriorityClass = _appConfig.GetProcessPriority(); // Fire the Encode Started Event InvokeEncodeStarted(EventArgs.Empty); } catch (Exception exc) { Log.Error(exc); _currentTask.ExitCode = -1; IsEncoding = false; InvokeEncodeCompleted(new EncodeCompletedEventArgs(false, exc, exc.Message)); } }
/// <summary> /// Generates AviSynth script used for video encoding /// </summary> /// <param name="videoInfo">All video properties</param> /// <param name="changeFps">Defines whether framerate should be changed</param> /// <param name="targetFps">Sets target framerate</param> /// <param name="resizeTo">Sets target video resolution</param> /// <param name="stereoEncoding">Defines, which stereo encoding mode should be used</param> /// <param name="stereoVideoInfo">Sets all parameters for stereo encoding</param> /// <param name="isDvdResolution">Defines whether target resolution is used for DVD encoding</param> /// <param name="subtitleFile">Sets subtitle file for hardcoding into video</param> /// <param name="subtitleOnlyForced">Defines whether only forced captions should be hardcoded</param> /// <param name="skipScaling"></param> /// <returns>Path to AviSynth script</returns> public string Generate(VideoInfo videoInfo, bool changeFps, float targetFps, Size resizeTo, StereoEncoding stereoEncoding, StereoVideoInfo stereoVideoInfo, bool isDvdResolution, string subtitleFile, bool subtitleOnlyForced, bool skipScaling) { var sb = new StringBuilder(); var mtUseful = (videoInfo.Interlaced && _appConfig.UseHQDeinterlace) || changeFps; var useStereo = stereoEncoding != StereoEncoding.None && stereoVideoInfo.RightStreamId > -1; // support for multithreaded AviSynth if (_appConfig.UseAviSynthMT && mtUseful) { sb.AppendLine("SetMTMode(2,0)"); sb.AppendLine("SetMemoryMax(512)"); } var pluginList = new List <string> { "ffms2.dll" }; var scriptList = new List <string>(); //loading plugins if (changeFps || (videoInfo.Interlaced && _appConfig.UseHQDeinterlace)) { pluginList.Add("mvtools2.dll"); } if (videoInfo.Interlaced && _appConfig.UseHQDeinterlace) { pluginList.Add(_appConfig.LastAviSynthVer.StartsWith("2.5") ? "mt_masktools-25.dll" : "mt_masktools-26.dll"); pluginList.Add("nnedi3.dll"); pluginList.Add("RemoveGrainSSE2.dll"); pluginList.Add("RepairSSE2.dll"); scriptList.Add("QTGMC-3.32.avsi"); } else if (videoInfo.Interlaced) { pluginList.Add("Decomb.dll"); } if (useStereo) { pluginList.Add("H264StereoSource.dll"); } if (!string.IsNullOrEmpty(subtitleFile) && File.Exists(subtitleFile)) { switch (Path.GetExtension(subtitleFile)) { case "sup": pluginList.Add("SupTitle.dll"); break; case "ass": case "ssa": case "srt": pluginList.Add("VSFilter.dll"); break; } } // generate plugin and script loading foreach (var plugin in pluginList) { sb.AppendLine($"LoadPlugin(\"{Path.Combine(_appConfig.AvsPluginsPath, plugin)}\")"); } foreach (var script in scriptList) { sb.AppendLine($"Import(\"{Path.Combine(_appConfig.AvsPluginsPath, script)}\")"); } //generate rest of the script // calculate framerate numerator & denominator if (videoInfo.Fps <= 0) { var mi = new MediaInfoContainer(); try { mi = GenHelper.GetMediaInfo(videoInfo.TempFile); } catch (TimeoutException ex) { Log.Error(ex); mi = new MediaInfoContainer(); } finally { if (mi.Video.Count > 0) { videoInfo.Fps = mi.Video[0].FrameRate; VideoHelper.GetFpsNumDenom(videoInfo.Fps, out videoInfo.FrameRateEnumerator, out videoInfo.FrameRateDenominator); if (videoInfo.FrameRateEnumerator == 0) { videoInfo.FrameRateEnumerator = (int)Math.Round(videoInfo.Fps) * 1000; videoInfo.FrameRateDenominator = (int)(Math.Round(Math.Ceiling(videoInfo.Fps) - Math.Floor(videoInfo.Fps)) + 1000); } } } } sb.Append($"FFVideoSource(\"{videoInfo.TempFile}\","); if (videoInfo.FrameRateEnumerator > 0 && videoInfo.FrameRateDenominator > 0) { sb.Append($"fpsnum={videoInfo.FrameRateEnumerator:0},fpsden={videoInfo.FrameRateDenominator:0},"); } var threadCount = _appConfig.LimitDecoderThreads ? 1 : 0; sb.Append($"threads={threadCount:0})"); sb.AppendLine(); var stereoVar = string.Empty; if (useStereo) { var configFile = GenerateStereoSourceConfig(stereoVideoInfo); sb.AppendLine($"VideoRight = H264StereoSource(\"{configFile}\",{videoInfo.FrameCount - 50:0})"); StereoConfigFile = configFile; stereoVar = "VideoRight"; } // deinterlace video source if (videoInfo.Interlaced) { if (_appConfig.UseHQDeinterlace) { sb.AppendLine("QTGMC(Preset=\"Slower\")"); } else { sb.AppendLine("ConvertToYUY2(interlaced=true)"); sb.AppendLine("Telecide(post=4)"); sb.AppendLine("Crop(4, 0, -4, 0)"); sb.AppendLine("AddBorders(4, 0, 4, 0)"); sb.AppendLine("ConvertToYV12()"); } } // hardcode subtitles if (!string.IsNullOrEmpty(subtitleFile) && File.Exists(subtitleFile)) { switch (Path.GetExtension(subtitleFile)) { case "sup": var subForced = subtitleOnlyForced ? "true" : "false"; sb.Append($"SupTitle(\"{subtitleFile}\", forcedOnly={subForced})"); break; case "ass": case "ssa": case "srt": sb.Append($"TextSub(\"{subtitleFile}\")"); break; } sb.AppendLine(); } // video cropping if (!videoInfo.CropRect.IsEmpty && !skipScaling) { int temp; Math.DivRem(videoInfo.CropRect.X, 2, out temp); videoInfo.CropRect.X += temp; Math.DivRem(videoInfo.CropRect.Y, 2, out temp); videoInfo.CropRect.Y += temp; Math.DivRem(videoInfo.CropRect.Width, 2, out temp); videoInfo.CropRect.Width += temp; Math.DivRem(videoInfo.CropRect.Height, 2, out temp); videoInfo.CropRect.Height += temp; videoInfo.Height = videoInfo.CropRect.Height; videoInfo.Width = videoInfo.CropRect.Width; if ((videoInfo.CropRect.X > 0) || (videoInfo.CropRect.Y > 0) || (videoInfo.CropRect.Width < videoInfo.Width) || (videoInfo.CropRect.Height < videoInfo.Height)) { sb.Append(useStereo ? "CroppedVideoRight = Crop(VideoRight," : "Crop("); sb.Append($"{videoInfo.CropRect.Left:0},{videoInfo.CropRect.Top:0},"); sb.Append($"{videoInfo.CropRect.Width:0},{videoInfo.CropRect.Height:0})"); sb.AppendLine(); if (useStereo) { stereoVar = "CroppedVideoRight"; } } } // Side-By-Side stereo encoding if (!string.IsNullOrEmpty(stereoVar)) { switch (stereoEncoding) { case StereoEncoding.FullSideBySideLeft: case StereoEncoding.HalfSideBySideLeft: sb.AppendLine($"StackHorizontal(last,{stereoVar})"); break; case StereoEncoding.FullSideBySideRight: case StereoEncoding.HalfSideBySideRight: sb.AppendLine($"StackHorizontal({stereoVar},last)"); break; } sb.AppendLine("ConvertToYV12()"); } var calculatedHeight = videoInfo.Height; var calculatedWidth = videoInfo.Width; var borderRight = 0; var borderLeft = 0; var borderBottom = 0; var borderTop = 0; var addBorders = false; // video resizing if (!resizeTo.IsEmpty && (resizeTo.Height != videoInfo.Height || resizeTo.Width != videoInfo.Width) && !skipScaling) { // aspect ratios var toAr = (float)Math.Round(resizeTo.Width / (float)resizeTo.Height, 3); var fromAr = videoInfo.AspectRatio; var mod = 1f; calculatedWidth = resizeTo.Width; if (fromAr > toAr) // source aspectratio higher than target aspectratio { if (isDvdResolution) { calculatedHeight = (int)(calculatedWidth / fromAr); if (calculatedHeight > resizeTo.Height) { calculatedHeight = resizeTo.Height; } calculatedWidth = 720; } else { calculatedWidth = resizeTo.Width; calculatedHeight = (int)(calculatedWidth / fromAr); } int temp; Math.DivRem(calculatedWidth, 2, out temp); calculatedWidth += temp; Math.DivRem(calculatedHeight, 2, out temp); calculatedHeight += temp; if (calculatedHeight != resizeTo.Height) { addBorders = true; var borderHeight = resizeTo.Height - calculatedHeight; borderTop = borderHeight / 2; Math.DivRem(borderTop, 2, out temp); borderTop += temp; borderBottom = borderHeight - borderTop; } } else if (Math.Abs(fromAr - toAr) <= 0) // source and target aspectratio equals { if (isDvdResolution) { calculatedHeight = (int)(calculatedWidth / fromAr); calculatedWidth = 720; if (calculatedHeight > resizeTo.Height) { calculatedHeight = resizeTo.Height; } } else { calculatedWidth = resizeTo.Width; calculatedHeight = (int)(calculatedWidth / toAr); } int temp; Math.DivRem(calculatedWidth, 2, out temp); calculatedWidth += temp; Math.DivRem(calculatedHeight, 2, out temp); calculatedHeight += temp; if (calculatedHeight != resizeTo.Height) { addBorders = true; var borderHeight = resizeTo.Height - calculatedHeight; borderTop = borderHeight / 2; Math.DivRem(borderTop, 2, out temp); borderTop += temp; borderBottom = borderHeight - borderTop; } } else { if (fromAr > 1.4f && isDvdResolution) // source aspectratio not 4:3, encoding for dvd resolution { mod = 720f / resizeTo.Width; calculatedHeight = (int)(calculatedWidth / fromAr); if (calculatedHeight > resizeTo.Height) { calculatedHeight = resizeTo.Height; calculatedWidth = (int)(calculatedHeight * fromAr * mod); } else { calculatedWidth = 720; } } else if (isDvdResolution) { calculatedHeight = resizeTo.Height; calculatedWidth = (int)(calculatedHeight * fromAr); } else { calculatedHeight = resizeTo.Height; } int temp; Math.DivRem(calculatedWidth, 2, out temp); calculatedWidth += temp; Math.DivRem(calculatedHeight, 2, out temp); calculatedHeight += temp; if (Math.Abs(toAr - 1.778f) <= 0) // aspectratio 16:9 { addBorders = true; var borderHeight = resizeTo.Height - calculatedHeight; borderTop = borderHeight / 2; Math.DivRem(borderTop, 2, out temp); borderTop += temp; borderBottom = borderHeight - borderTop; var borderWidth = (int)((resizeTo.Width * mod) - calculatedWidth); borderLeft = borderWidth / 2; Math.DivRem(borderLeft, 2, out temp); borderLeft += temp; borderRight = borderWidth - borderLeft; } else if (calculatedWidth != resizeTo.Width) { addBorders = true; var borderWidth = resizeTo.Width - calculatedWidth; borderLeft = borderWidth / 2; Math.DivRem(borderLeft, 2, out temp); borderLeft += temp; borderRight = borderWidth - borderLeft; var borderHeight = resizeTo.Height - calculatedHeight; borderTop = borderHeight / 2; Math.DivRem(borderTop, 2, out temp); borderTop += temp; borderBottom = borderHeight - borderTop; } } } // apply resize filter if (calculatedHeight != videoInfo.Height || calculatedWidth != videoInfo.Width || (stereoEncoding == StereoEncoding.HalfSideBySideLeft || stereoEncoding == StereoEncoding.HalfSideBySideRight && useStereo) && !skipScaling) { if (calculatedHeight < videoInfo.Height || calculatedWidth < videoInfo.Width || (stereoEncoding == StereoEncoding.HalfSideBySideLeft || stereoEncoding == StereoEncoding.HalfSideBySideRight && useStereo)) { sb.Append("BicubicResize"); } else { sb.Append("Lanczos4Resize"); } sb.Append($"({calculatedWidth:0},{calculatedHeight:0})"); sb.AppendLine(); } // add borders if needed if (addBorders && (borderLeft > 0 || borderRight > 0 || borderTop > 0 || borderBottom > 0) && !skipScaling) { sb.AppendLine($"AddBorders({borderLeft:0},{borderTop:0},{borderRight:0},{borderBottom:0})"); } // change framerate if (changeFps) { int fpsnum; int fpsden; // get framerate numerator & denominator for target framerate VideoHelper.GetFpsNumDenom(targetFps, out fpsnum, out fpsden); // source is 23.976 or 24 fps if (videoInfo.FrameRateEnumerator == 24000 && (videoInfo.FrameRateDenominator == 1001 || videoInfo.FrameRateDenominator == 1000)) { if (fpsnum == 30000 && fpsden == 1001) { // 3:2 pulldown / telecine sb.AppendLine("AssumeFrameBased()"); sb.AppendLine("SeparateFields()"); sb.AppendLine("SelectEvery(8, 0, 1, 2, 3, 2, 5, 4, 7, 6, 7)"); sb.AppendLine("Weave()"); } else if (fpsnum == 25000 && fpsden == 1000) { // convert to 25 fps sb.AppendLine("ConvertToYUY2()"); sb.AppendLine("ConvertFPS(50)"); sb.AppendLine("AssumeTFF()"); sb.AppendLine("SeparateFields()"); sb.AppendLine("SelectEvery(4,0,3)"); sb.AppendLine("Weave()"); sb.AppendLine("ConvertToYV12()"); } } // source is 30fps else if (videoInfo.FrameRateEnumerator == 30000) { sb.AppendLine("ConvertToYUY2()"); sb.AppendLine("DoubleWeave()"); sb.AppendLine($"ConvertFPS(numerator={fpsnum * 2:0},denominator={fpsden:0})"); sb.AppendLine("SelectEven()"); sb.AppendLine("ConvertToYV12()"); } // source is 25fps else if (videoInfo.FrameRateEnumerator == 25000 && videoInfo.FrameRateDenominator == 1000) { if ((fpsnum == 30000 || fpsnum == 24000) && fpsden == 1001) { sb.AppendLine("ConvertToYUY2()"); sb.AppendLine("ConvertFPS(numerator=48000,denominator=1001"); if (fpsnum == 30000) { sb.AppendLine("AssumeFrameBased()"); sb.AppendLine("SeparateFields()"); sb.AppendLine("SelectEvery(8, 0, 1, 2, 3, 2, 5, 4, 7, 6, 7)"); } else { sb.AppendLine("AssumeTFF()"); sb.AppendLine("SeparateFields()"); sb.AppendLine("SelectEven()"); } sb.AppendLine("Weave()"); sb.AppendLine("ConvertToYV12()"); } } // every other framerate else { // very slow framerate interpolation sb.AppendLine("super = MSuper(pel=2)"); sb.AppendLine("backward_vec = MAnalyse(super, overlap=4, isb = true, search=3)"); sb.AppendLine("forward_vec = MAnalyse(super, overlap=4, isb = false, search=3)"); sb.Append($"MFlowFps(super, backward_vec, forward_vec, num={fpsnum:0}, den={fpsden:0})"); } sb.AppendLine(); } // multithreaded avisynth if (!_appConfig.UseAviSynthMT || !mtUseful) { return(WriteScript(sb.ToString())); } sb.AppendLine("SetMTMode(1)"); sb.AppendLine("GetMTMode(false) > 0 ? distributor() : last"); return(WriteScript(sb.ToString())); }