/// <summary> /// Generates configuration file used by H264StereoSource plugin /// </summary> /// <param name="stereoVideoInfo"></param> /// <returns></returns> private static string GenerateStereoSourceConfig(StereoVideoInfo stereoVideoInfo) { var sb = new StringBuilder(); sb.AppendLine($"InputFile = \"{stereoVideoInfo.LeftTempFile}\""); sb.AppendLine($"InputFile2 = \"{stereoVideoInfo.RightTempFile}\""); sb.AppendLine("FileFormat = 0"); sb.AppendLine("POCScale = 1"); sb.AppendLine("DisplayDecParams = 1"); sb.AppendLine("ConcealMode = 0"); sb.AppendLine("RefPOCGap = 2"); sb.AppendLine("POCGap = 2"); sb.AppendLine("IntraProfileDeblocking = 1"); sb.AppendLine("DecFrmNum = 0"); return(WriteScript(sb.ToString(), "cfg")); }
public void GetBdInfo() { const string strChapters = "Chapters"; //ProcessingService.GetResourceString("streamselect_chapters"); const string strVideo = "Video"; //ProcessingService.GetResourceString("streamselect_video"); const string strAudio = "Audio"; //ProcessingService.GetResourceString("streamselect_audio"); const string strSubtitles = "Subtitles"; //ProcessingService.GetResourceString("streamselect_subtitles"); _bdInfo = new BDROM(JobInfo.InputFile); _bdInfo.Scan(); var longestClip = GetLongestBdPlaylist(); var playlistIndex = 1; foreach (var item in _bdInfo.PlaylistFiles.Values) { if (!item.IsValid) { playlistIndex++; continue; } var streamIndex = 0; var duration = new DateTime(); duration = duration.AddSeconds(item.TotalLength); var treeRoot = $"Title: {playlistIndex:0} ({item.Name}), Length: {duration.ToString("H:mm:ss.fff")}"; var treeData = new Dictionary <string, object> { { "Name", Path.Combine(_bdInfo.DirectoryPLAYLIST.FullName, item.Name) }, { "PlaylistIndex", playlistIndex } }; var root = new StreamTreeNode { ID = _treeNodeID++, Name = treeRoot, Data = treeData, Children = new List <StreamTreeNode>(), IsChecked = true, IsExpanded = true }; 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); var streamChapters = new List <TimeSpan>(); if (item.Chapters.Count > 1) { streamIndex++; streamChapters.AddRange(item.Chapters.Select(TimeSpan.FromSeconds)); var chaptersFormat = $"{streamChapters.Count:0} {strChapters}"; CreateNode(chaptersStreamTree, chaptersFormat, streamChapters); } var videoDescStereo = string.Empty; var leftVideoStreamID = -1; var leftVideoDiscStreamID = -1; foreach (var clip in item.VideoStreams) { streamIndex++; var videoCodec = clip.CodecName; var videoCodecShort = clip.CodecShortName; var videoDesc = clip.Description; if ((clip.StreamType == TSStreamType.AVC_VIDEO) && (item.VideoStreams.Count > 1) && (item.VideoStreams[0].PID == clip.PID) && (item.VideoStreams[item.VideoStreams.Count - 1].StreamType == TSStreamType.MVC_VIDEO)) { videoDescStereo = videoDesc; videoCodec += item.MVCBaseViewR ? " (right eye)" : " (left eye)"; leftVideoStreamID = streamIndex; leftVideoDiscStreamID = clip.PID; } if ((clip.StreamType == TSStreamType.MVC_VIDEO) && (item.VideoStreams.Count > 1) && (item.VideoStreams[item.VideoStreams.Count - 1].PID == clip.PID) && (item.VideoStreams[0].StreamType == TSStreamType.AVC_VIDEO)) { videoDesc = videoDescStereo; videoCodec = "MPEG-4 MVC Video"; videoCodec += item.MVCBaseViewR ? " (right eye)" : " (left eye)"; } var videoStreamFormat = $"{streamIndex:0}: {videoCodec} ({videoCodecShort}), {videoDesc}"; switch (clip.StreamType) { case TSStreamType.AVC_VIDEO: case TSStreamType.MPEG2_VIDEO: case TSStreamType.MPEG1_VIDEO: case TSStreamType.VC1_VIDEO: { var vid = new VideoInfo { StreamId = streamIndex, TrackId = playlistIndex, Fps = (float)clip.FrameRateEnumerator / clip.FrameRateDenominator, PicSize = (VideoFormat)clip.VideoFormat, Interlaced = clip.IsInterlaced, Format = clip.CodecShortName, DemuxStreamId = clip.PID, FrameCount = 0, Encoded = false, IsRawStream = false, StreamSize = 0, Length = item.TotalLength, FrameRateEnumerator = clip.FrameRateEnumerator, FrameRateDenominator = clip.FrameRateDenominator, Height = clip.Height }; int.TryParse(item.Name.Substring(0, item.Name.LastIndexOf('.')), NumberStyles.Number, _configService.CInfo, out vid.DemuxPlayList); foreach (var streamClip in item.StreamClips) { vid.DemuxStreamNames.Add(streamClip.StreamFile.FileInfo.FullName); } float mod; switch (clip.AspectRatio) { case TSAspectRatio.ASPECT_16_9: mod = (float)1.777778; break; default: mod = (float)1.333333; break; } vid.Width = (int)(vid.Height * mod); vid.AspectRatio = mod; CreateNode(videoStreamTree, videoStreamFormat, vid); } break; case TSStreamType.MVC_VIDEO: { var vid = new StereoVideoInfo { RightStreamId = streamIndex, DemuxRightStreamId = clip.PID, LeftStreamId = leftVideoStreamID, DemuxLeftStreamId = leftVideoDiscStreamID }; CreateNode(videoStreamTree, videoStreamFormat, vid); } break; } } foreach (var audio in item.AudioStreams) { streamIndex++; var audioCodec = audio.CodecName; var audioCodecShort = audio.CodecShortName; var audioDesc = audio.Description; var audioLangCode = audio.LanguageCode; var audioLanguage = audio.LanguageName; var audioStreamFormat = $"{streamIndex:0}: {audioCodec} ({audioCodecShort}) / {audioLangCode} ({audioLanguage}) / {audioDesc}"; var aud = new AudioInfo { Format = audioCodecShort, FormatProfile = string.Empty, Id = streamIndex, StreamId = streamIndex, LangCode = audioLangCode, TempFile = string.Empty, OriginalId = streamIndex, Delay = 0, Bitrate = audio.BitRate, DemuxStreamId = audio.PID, SampleRate = audio.SampleRate, ChannelCount = audio.ChannelCount + audio.LFE, BitDepth = audio.BitDepth, ShortLang = audio.LanguageCode, StreamSize = 0, Length = item.TotalLength, IsHdStream = audio.CoreStream != null }; CreateNode(audioStreamTree, audioStreamFormat, aud); } foreach (var sub in item.TextStreams) { streamIndex++; var subCodecShort = sub.CodecShortName; var subDesc = sub.Description; var subLangCode = sub.LanguageCode; var subLanguage = sub.LanguageName; var subStreamFormat = $"{streamIndex:0}: {subCodecShort} / {subLangCode} ({subLanguage}); {subDesc}"; var subInfo = new SubtitleInfo { Id = streamIndex, StreamId = streamIndex, TempFile = string.Empty, LangCode = subLangCode, Format = subCodecShort, Delay = 0, DemuxStreamId = sub.PID, StreamSize = 0 }; CreateNode(subStreamTree, subStreamFormat, subInfo); } foreach (var sub in item.GraphicsStreams) { streamIndex++; var subCodecShort = sub.CodecShortName; var subDesc = sub.Description; var subLangCode = sub.LanguageCode; var subLanguage = sub.LanguageName; var subStreamFormat = $"{streamIndex:0}: {subCodecShort} / {subLangCode} ({subLanguage}); {subDesc}"; var subInfo = new SubtitleInfo { Id = streamIndex, StreamId = streamIndex, TempFile = string.Empty, LangCode = subLangCode, Format = subCodecShort, DemuxStreamId = sub.PID, StreamSize = 0 }; CreateNode(subStreamTree, subStreamFormat, subInfo); } playlistIndex++; } _defaultSelection = longestClip - 1; }
/// <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())); }
/// <summary> /// Generates configuration file used by H264StereoSource plugin /// </summary> /// <param name="stereoVideoInfo"></param> /// <returns></returns> private static string GenerateStereoSourceConfig(StereoVideoInfo stereoVideoInfo) { StringBuilder sb = new StringBuilder(); sb.AppendFormat(AppSettings.CInfo, "InputFile = \"{0:s}\"", stereoVideoInfo.LeftTempFile); sb.AppendLine(); sb.AppendFormat(AppSettings.CInfo, "InputFile2 = \"{0:s}\"", stereoVideoInfo.RightTempFile); sb.AppendLine(); sb.AppendLine("FileFormat = 0"); sb.AppendLine("POCScale = 1"); sb.AppendLine("DisplayDecParams = 1"); sb.AppendLine("ConcealMode = 0"); sb.AppendLine("RefPOCGap = 2"); sb.AppendLine("POCGap = 2"); sb.AppendLine("IntraProfileDeblocking = 1"); sb.AppendLine("DecFrmNum = 0"); return (WriteScript(sb.ToString(), "cfg")); }
/// <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> /// <returns>Path to AviSynth script</returns> public static string Generate(VideoInfo videoInfo, bool changeFps, float targetFps, Size resizeTo, StereoEncoding stereoEncoding, StereoVideoInfo stereoVideoInfo, bool isDvdResolution, string subtitleFile, bool subtitleOnlyForced) { StringBuilder sb = new StringBuilder(); bool mtUseful = (videoInfo.Interlaced && AppSettings.UseHQDeinterlace) || changeFps; bool useStereo = stereoEncoding != StereoEncoding.None && stereoVideoInfo.RightStreamId > -1; // support for multithreaded AviSynth if (AppSettings.UseAviSynthMT && mtUseful) { sb.AppendLine("SetMTMode(2,0)"); sb.AppendLine("SetMemoryMax(512)"); } //loading plugins sb.AppendLine(ImportFFMPEGSource()); // ffms2 if (changeFps || (videoInfo.Interlaced && AppSettings.UseHQDeinterlace)) sb.AppendLine(string.Format(AppSettings.CInfo, "LoadPlugin(\"{0:s}\")", Path.Combine(AppSettings.AppPath, "AvsPlugins", "mvtools2.dll"))); if (videoInfo.Interlaced && AppSettings.UseHQDeinterlace) { sb.AppendLine(AppSettings.LastAviSynthVer.StartsWith("2.5") ? string.Format(AppSettings.CInfo, "LoadPlugin(\"{0:s}\")", Path.Combine(AppSettings.AppPath, "AvsPlugins", "mt_masktools-25.dll")) : string.Format(AppSettings.CInfo, "LoadPlugin(\"{0:s}\")", Path.Combine(AppSettings.AppPath, "AvsPlugins", "mt_masktools-26.dll"))); sb.AppendLine(string.Format(AppSettings.CInfo, "LoadPlugin(\"{0:s}\")", Path.Combine(AppSettings.AppPath, "AvsPlugins", "nnedi3.dll"))); sb.AppendLine(string.Format(AppSettings.CInfo, "LoadPlugin(\"{0:s}\")", Path.Combine(AppSettings.AppPath, "AvsPlugins", "RemoveGrainSSE2.dll"))); sb.AppendLine(string.Format(AppSettings.CInfo, "LoadPlugin(\"{0:s}\")", Path.Combine(AppSettings.AppPath, "AvsPlugins", "RepairSSE2.dll"))); sb.AppendLine(string.Format(AppSettings.CInfo, "Import(\"{0:s}\")", Path.Combine(AppSettings.AppPath, "AvsPlugins", "QTGMC-3.32.avsi"))); } else if (videoInfo.Interlaced) { sb.AppendLine(string.Format(AppSettings.CInfo, "LoadPlugin(\"{0:s}\")", Path.Combine(AppSettings.AppPath, "AvsPlugins", "Decomb.dll"))); } if (useStereo) sb.AppendLine(string.Format(AppSettings.CInfo, "LoadPlugin(\"{0:s}\")", Path.Combine(AppSettings.AppPath, "AvsPlugins", "H264StereoSource.dll"))); if (!string.IsNullOrEmpty(subtitleFile) && File.Exists(subtitleFile)) { switch (Path.GetExtension(subtitleFile)) { case "sup": sb.AppendFormat(AppSettings.CInfo, "LoadPlugin(\"{0:s}\")", Path.Combine(AppSettings.AppPath, "AvsPlugins", "SupTitle.dll")); break; case "ass": case "ssa": case "srt": sb.AppendFormat(AppSettings.CInfo, "LoadPlugin(\"{0:s}\")", Path.Combine(AppSettings.AppPath, "AvsPlugins", "VSFilter.dll")); break; } sb.AppendLine(); } //generate rest of the script // calculate framerate numerator & denominator if (videoInfo.FPS <= 0) { MediaInfoContainer mi = new MediaInfoContainer(); try { mi = Processing.GetMediaInfo(videoInfo.TempFile); } catch (TimeoutException ex) { Log.Error(ex); mi = new MediaInfoContainer(); } finally { if (mi.Video.Count > 0) { videoInfo.FPS = mi.Video[0].FrameRate; Processing.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); } } } } if (videoInfo.FrameRateEnumerator > 0 && videoInfo.FrameRateDenominator > 0) sb.AppendLine(string.Format(AppSettings.CInfo, "FFVideoSource(\"{0:s}\",fpsnum={1:0},fpsden={2:0},threads=1)", videoInfo.TempFile, videoInfo.FrameRateEnumerator, videoInfo.FrameRateDenominator)); else sb.AppendLine(string.Format(AppSettings.CInfo, "FFVideoSource(\"{0:s}\",threads=1)", videoInfo.TempFile)); string stereoVar = string.Empty; if (useStereo) { string configFile = GenerateStereoSourceConfig(stereoVideoInfo); sb.AppendLine(string.Format(AppSettings.CInfo, "VideoRight = H264StereoSource(\"{0:s}\",{1:g})", configFile, videoInfo.FrameCount - 50)); StereoConfigFile = configFile; stereoVar = "VideoRight"; } // deinterlace video source if (videoInfo.Interlaced) { if (AppSettings.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": sb.AppendFormat(AppSettings.CInfo, "SupTitle(\"{0}\", forcedOnly={1})", subtitleFile, subtitleOnlyForced ? "true" : "false"); break; case "ass": case "ssa": case "srt": sb.AppendFormat(AppSettings.CInfo, "TextSub(\"{0}\")", subtitleFile); break; } sb.AppendLine(); } // video cropping if (!videoInfo.CropRect.IsEmpty) { 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.AppendLine(string.Format(AppSettings.CInfo, "Crop({0:g},{1:g},{2:g},{3:g})", videoInfo.CropRect.Left, videoInfo.CropRect.Top, videoInfo.CropRect.Width, videoInfo.CropRect.Height)); if (useStereo) { sb.AppendLine(string.Format(AppSettings.CInfo, "CroppedVideoRight = Crop(VideoRight,{0:g},{1:g},{2:g},{3:g})", videoInfo.CropRect.Left, videoInfo.CropRect.Top, videoInfo.CropRect.Width, videoInfo.CropRect.Height)); stereoVar = "CroppedVideoRight"; } } } // Side-By-Side stereo encoding if (!string.IsNullOrEmpty(stereoVar)) { switch(stereoEncoding) { case StereoEncoding.FullSideBySideLeft: case StereoEncoding.HalfSideBySideLeft: sb.AppendLine(string.Format("StackHorizontal(last,{0})", stereoVar)); break; case StereoEncoding.FullSideBySideRight: case StereoEncoding.HalfSideBySideRight: sb.AppendLine(string.Format("StackHorizontal({0},last)", stereoVar)); break; } sb.AppendLine("ConvertToYV12()"); } int calculatedHeight = videoInfo.Height; int calculatedWidth = videoInfo.Width; int borderRight = 0; int borderLeft = 0; int borderBottom = 0; int borderTop = 0; bool addBorders = false; // video resizing if (!resizeTo.IsEmpty && (resizeTo.Height != videoInfo.Height || resizeTo.Width != videoInfo.Width)) { // aspect ratios float toAr = (float) Math.Round(resizeTo.Width / (float)resizeTo.Height, 3); calculatedWidth = resizeTo.Width; float mod = 1f; if (videoInfo.AspectRatio > toAr) // source aspectratio higher than target aspectratio { if (isDvdResolution) { calculatedHeight = (int)(calculatedWidth / videoInfo.AspectRatio); if (calculatedHeight > resizeTo.Height) calculatedHeight = resizeTo.Height; calculatedWidth = 720; } else { calculatedWidth = resizeTo.Width; calculatedHeight = (int)(calculatedWidth / videoInfo.AspectRatio); } int temp; Math.DivRem(calculatedWidth, 2, out temp); calculatedWidth += temp; Math.DivRem(calculatedHeight, 2, out temp); calculatedHeight += temp; if (calculatedHeight != resizeTo.Height) { addBorders = true; int borderHeight = resizeTo.Height - calculatedHeight; borderTop = borderHeight/2; Math.DivRem(borderTop, 2, out temp); borderTop += temp; borderBottom = borderHeight - borderTop; } } else if (Math.Abs(videoInfo.AspectRatio - toAr) <= 0) // source and target aspectratio equals { if (isDvdResolution) { calculatedHeight = (int)(calculatedWidth / videoInfo.AspectRatio); 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; int borderHeight = resizeTo.Height - calculatedHeight; borderTop = borderHeight/2; Math.DivRem(borderTop, 2, out temp); borderTop += temp; borderBottom = borderHeight - borderTop; } } else { if (videoInfo.AspectRatio > 1.4f && isDvdResolution) // source aspectratio not 4:3, encoding for dvd resolution { mod = 720f/resizeTo.Width; calculatedHeight = (int)(calculatedWidth / videoInfo.AspectRatio); if (calculatedHeight > resizeTo.Height) { calculatedHeight = resizeTo.Height; calculatedWidth = (int)(calculatedHeight * videoInfo.AspectRatio * mod); } else calculatedWidth = 720; } else if (isDvdResolution) { calculatedHeight = resizeTo.Height; calculatedWidth = (int) (calculatedHeight*videoInfo.AspectRatio); } 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; int borderHeight = resizeTo.Height - calculatedHeight; borderTop = borderHeight/2; Math.DivRem(borderTop, 2, out temp); borderTop += temp; borderBottom = borderHeight - borderTop; int 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; int borderWidth = resizeTo.Width - calculatedWidth; borderLeft = borderWidth/2; Math.DivRem(borderLeft, 2, out temp); borderLeft += temp; borderRight = borderWidth - borderLeft; int 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)) { if (calculatedHeight < videoInfo.Height || calculatedWidth < videoInfo.Width || (stereoEncoding == StereoEncoding.HalfSideBySideLeft || stereoEncoding == StereoEncoding.HalfSideBySideRight && useStereo)) sb.AppendLine(string.Format(AppSettings.CInfo, "BicubicResize({0:g},{1:g})", calculatedWidth, calculatedHeight)); else sb.AppendLine(string.Format(AppSettings.CInfo, "Lanczos4Resize({0:g},{1:g})", calculatedWidth, calculatedHeight)); } // add borders if needed if (addBorders && (borderLeft > 0 || borderRight > 0 || borderTop > 0 || borderBottom > 0)) sb.AppendLine(string.Format(AppSettings.CInfo, "AddBorders({0:g},{1:g},{2:g},{3:g})", borderLeft, borderTop, borderRight, borderBottom)); // change framerate if (changeFps) { int fpsnum; int fpsden; // get framerate numerator & denominator for target framerate Processing.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(string.Format(AppSettings.CInfo, "ConvertFPS({0:0.000})", targetFps*2)); 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(string.Format(AppSettings.CInfo, "ConvertFPS({0:0.000}*2)", 23.976)); 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.AppendFormat("MFlowFps(super, backward_vec, forward_vec, num={0:0}, den={1:0})", fpsnum, fpsden); } sb.AppendLine(); } // multithreaded avisynth if (AppSettings.UseAviSynthMT && mtUseful) { sb.AppendLine("SetMTMode(1)"); sb.AppendLine("GetMTMode(false) > 0 ? distributor() : last"); } return WriteScript(sb.ToString()); }