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);
            }
        }
Exemple #2
0
        /// <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));
        }
Exemple #3
0
        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()));
        }