internal string ToShortStringHHMMSSFF() { if (_time.Minutes == 0 && _time.Hours == 0) { return(string.Format("{0:00}:{1:00}", _time.Seconds, SubtitleFormat.MillisecondsToFrames(_time.Milliseconds))); } if (_time.Hours == 0) { return(string.Format("{0:00}:{1:00}:{2:00}", _time.Minutes, _time.Seconds, SubtitleFormat.MillisecondsToFrames(_time.Milliseconds))); } return(string.Format("{0:00}:{1:00}:{2:00}:{3:00}", _time.Hours, _time.Minutes, _time.Seconds, SubtitleFormat.MillisecondsToFrames(_time.Milliseconds))); }
public static int BridgeGaps(Subtitle subtitle, int minMsBetweenLines, bool divideEven, double maxMs, List <int> fixedIndexes, Dictionary <string, string> dic, bool useFrames) { int fixedCount = 0; if (minMsBetweenLines > maxMs) { string message = $"{nameof(DurationsBridgeGaps)}: {nameof(minMsBetweenLines)} cannot be larger than {nameof(maxMs)}!"; SeLogger.Error(new InvalidOperationException(message), message); return(0); } int count = subtitle.Paragraphs.Count - 1; for (int i = 0; i < count; i++) { var cur = subtitle.Paragraphs[i]; var next = subtitle.Paragraphs[i + 1]; double currentGap = next.StartTime.TotalMilliseconds - cur.EndTime.TotalMilliseconds; // there shouldn't be adjustment if current gaps is shorter or equal than minimum gap or greater than maximum gaps if (currentGap <= minMsBetweenLines || currentGap > maxMs) { continue; } // next paragraph start-time will be pull to try to meet the current paragraph if (divideEven) { next.StartTime.TotalMilliseconds -= currentGap / 2.0; } cur.EndTime.TotalMilliseconds = next.StartTime.TotalMilliseconds - minMsBetweenLines; if (fixedIndexes != null) { fixedIndexes.Add(i); fixedIndexes.Add(i + 1); } fixedCount++; double newGap = next.StartTime.TotalMilliseconds - cur.EndTime.TotalMilliseconds; if (useFrames) { dic?.Add(cur.Id, $"{SubtitleFormat.MillisecondsToFrames(currentGap)} => {SubtitleFormat.MillisecondsToFrames(newGap)}"); } else { dic?.Add(cur.Id, $"{currentGap / TimeCode.BaseUnit:0.000} => {newGap / TimeCode.BaseUnit:0.000}"); } } return(fixedCount); }
public void MillisecondsToFrames() { Configuration.Settings.General.CurrentFrameRate = 23.976; var fr = SubtitleFormat.MillisecondsToFrames(100); Assert.AreEqual(2, fr); fr = SubtitleFormat.MillisecondsToFrames(999); Assert.AreEqual(24, fr); Configuration.Settings.General.CurrentFrameRate = 30; fr = SubtitleFormat.MillisecondsToFrames(100); Assert.AreEqual(3, fr); fr = SubtitleFormat.MillisecondsToFrames(999); Assert.AreEqual(30, fr); fr = SubtitleFormat.MillisecondsToFrames(2000); Assert.AreEqual(60, fr); }
/// <summary> /// Two frames gap minimum /// </summary> public void Check(Subtitle subtitle, NetflixQualityController controller) { if (controller.Language == "ja") { return; } for (int index = 0; index < subtitle.Paragraphs.Count; index++) { Paragraph p = subtitle.Paragraphs[index]; var next = subtitle.GetParagraphOrDefault(index + 1); double twoFramesGap = 1000.0 / controller.FrameRate * 2.0; if (next != null && SubtitleFormat.MillisecondsToFrames(p.EndTime.TotalMilliseconds + twoFramesGap) > SubtitleFormat.MillisecondsToFrames(next.StartTime.TotalMilliseconds) && !p.StartTime.IsMaxTime) { var fixedParagraph = new Paragraph(p, false) { EndTime = { TotalMilliseconds = next.StartTime.TotalMilliseconds - twoFramesGap } }; string comment = "Minimum two frames gap"; controller.AddRecord(p, fixedParagraph, comment); } } }
public void Check(Subtitle subtitle, NetflixQualityController controller) { if (controller.Language == "ja") { return; } for (int index = 0; index < subtitle.Paragraphs.Count; index++) { var p = subtitle.Paragraphs[index]; var next = subtitle.GetParagraphOrDefault(index + 1); if (next == null) { continue; } double twoFramesGap = 1000.0 / controller.FrameRate * 2.0; var gapInFrames = SubtitleFormat.MillisecondsToFrames(next.StartTime.TotalMilliseconds) - SubtitleFormat.MillisecondsToFrames(p.EndTime.TotalMilliseconds); if (gapInFrames >= 3 && gapInFrames <= 11 && !p.StartTime.IsMaxTime) { var fixedParagraph = new Paragraph(p, false) { EndTime = { TotalMilliseconds = next.StartTime.TotalMilliseconds - twoFramesGap } }; string comment = "3-11 frames gap => 2 frames gap"; controller.AddRecord(p, fixedParagraph, comment); } } }
internal static string GetParagraph(string template, string start, string end, string text, string translation, int number, string actor, TimeCode duration, string timeCodeTemplate, Paragraph p) { var cps = Utilities.GetCharactersPerSecond(p); var d = duration.ToString(); if (timeCodeTemplate == "ff" || timeCodeTemplate == "f") { d = SubtitleFormat.MillisecondsToFrames(duration.TotalMilliseconds).ToString(CultureInfo.InvariantCulture); } if (timeCodeTemplate == "zzz" || timeCodeTemplate == "zz" || timeCodeTemplate == "z") { d = duration.TotalMilliseconds.ToString(CultureInfo.InvariantCulture); } if (timeCodeTemplate == "sss" || timeCodeTemplate == "ss" || timeCodeTemplate == "s") { d = duration.Seconds.ToString(CultureInfo.InvariantCulture); } else if (timeCodeTemplate.EndsWith("ss.ff", StringComparison.Ordinal)) { d = $"{duration.Seconds:00}.{SubtitleFormat.MillisecondsToFramesMaxFrameRate(duration.Milliseconds):00}"; } else if (timeCodeTemplate.EndsWith("ss:ff", StringComparison.Ordinal)) { d = $"{duration.Seconds:00}:{SubtitleFormat.MillisecondsToFramesMaxFrameRate(duration.Milliseconds):00}"; } else if (timeCodeTemplate.EndsWith("ss,ff", StringComparison.Ordinal)) { d = $"{duration.Seconds:00},{SubtitleFormat.MillisecondsToFramesMaxFrameRate(duration.Milliseconds):00}"; } else if (timeCodeTemplate.EndsWith("ss;ff", StringComparison.Ordinal)) { d = $"{duration.Seconds:00};{SubtitleFormat.MillisecondsToFramesMaxFrameRate(duration.Milliseconds):00}"; } else if (timeCodeTemplate.EndsWith("ss.zzz", StringComparison.Ordinal)) { d = $"{duration.Seconds:00}.{duration.Milliseconds:000}"; } else if (timeCodeTemplate.EndsWith("ss:zzz", StringComparison.Ordinal)) { d = $"{duration.Seconds:00}:{duration.Milliseconds:000}"; } else if (timeCodeTemplate.EndsWith("ss,zzz", StringComparison.Ordinal)) { d = $"{duration.Seconds:00},{duration.Milliseconds:000}"; } else if (timeCodeTemplate.EndsWith("ss;zzz", StringComparison.Ordinal)) { d = $"{duration.Seconds:00};{duration.Milliseconds:000}"; } else if (timeCodeTemplate.EndsWith("ss.zz", StringComparison.Ordinal)) { d = $"{duration.Seconds:00}.{Math.Round(duration.Milliseconds / 10.0):00}"; } else if (timeCodeTemplate.EndsWith("ss:zz", StringComparison.Ordinal)) { d = $"{duration.Seconds:00}:{Math.Round(duration.Milliseconds / 10.0):00}"; } else if (timeCodeTemplate.EndsWith("ss,zz", StringComparison.Ordinal)) { d = $"{duration.Seconds:00},{Math.Round(duration.Milliseconds / 10.0):00}"; } else if (timeCodeTemplate.EndsWith("ss;zz", StringComparison.Ordinal)) { d = $"{duration.Seconds:00};{Math.Round(duration.Milliseconds / 10.0):00}"; } var lines = text.SplitToLines(); var line1 = string.Empty; var line2 = string.Empty; if (lines.Count > 0) { line1 = lines[0]; } if (lines.Count > 1) { line2 = lines[1]; } var s = template; s = s.Replace("{{", "@@@@_@@@{"); s = s.Replace("}}", "}@@@_@@@@"); s = string.Format(s, start, end, text, translation, number + 1, number, d, actor, line1, line2, cps.ToString(CultureInfo.InvariantCulture).Replace(".", ","), cps.ToString(CultureInfo.InvariantCulture), text.Length, p.Text.RemoveChar('\r', '\n').Length, p.Text.RemoveChar('\r', '\n').Length + lines.Count - 1, p.Text.RemoveChar('\r', '\n').Length + (lines.Count - 1) * 2); s = s.Replace("@@@@_@@@", "{"); s = s.Replace("@@@_@@@@", "}"); return(s); }
public static string GetTimeCode(TimeCode timeCode, string template) { var t = template; var templateTrimmed = t.Trim(); if (templateTrimmed == "ss") { t = t.Replace("ss", $"{timeCode.TotalSeconds:00}"); } if (templateTrimmed == "s") { t = t.Replace("s", $"{timeCode.TotalSeconds}"); } if (templateTrimmed == "zzz") { t = t.Replace("zzz", $"{timeCode.TotalMilliseconds:000}"); } if (templateTrimmed == "z") { t = t.Replace("z", $"{timeCode.TotalMilliseconds}"); } if (templateTrimmed == "ff") { t = t.Replace("ff", $"{SubtitleFormat.MillisecondsToFrames(timeCode.TotalMilliseconds)}"); } var totalSeconds = (int)timeCode.TotalSeconds; if (t.StartsWith("ssssssss", StringComparison.Ordinal)) { t = t.Replace("ssssssss", $"{totalSeconds:00000000}"); } if (t.StartsWith("sssssss", StringComparison.Ordinal)) { t = t.Replace("sssssss", $"{totalSeconds:0000000}"); } if (t.StartsWith("ssssss", StringComparison.Ordinal)) { t = t.Replace("ssssss", $"{totalSeconds:000000}"); } if (t.StartsWith("sssss", StringComparison.Ordinal)) { t = t.Replace("sssss", $"{totalSeconds:00000}"); } if (t.StartsWith("ssss", StringComparison.Ordinal)) { t = t.Replace("ssss", $"{totalSeconds:0000}"); } if (t.StartsWith("sss", StringComparison.Ordinal)) { t = t.Replace("sss", $"{totalSeconds:000}"); } if (t.StartsWith("ss", StringComparison.Ordinal)) { t = t.Replace("ss", $"{totalSeconds:00}"); } var totalMilliseconds = (long)timeCode.TotalMilliseconds; if (t.StartsWith("zzzzzzzz", StringComparison.Ordinal)) { t = t.Replace("zzzzzzzz", $"{totalMilliseconds:00000000}"); } if (t.StartsWith("zzzzzzz", StringComparison.Ordinal)) { t = t.Replace("zzzzzzz", $"{totalMilliseconds:0000000}"); } if (t.StartsWith("zzzzzz", StringComparison.Ordinal)) { t = t.Replace("zzzzzz", $"{totalMilliseconds:000000}"); } if (t.StartsWith("zzzzz", StringComparison.Ordinal)) { t = t.Replace("zzzzz", $"{totalMilliseconds:00000}"); } if (t.StartsWith("zzzz", StringComparison.Ordinal)) { t = t.Replace("zzzz", $"{totalMilliseconds:0000}"); } if (t.StartsWith("zzz", StringComparison.Ordinal)) { t = t.Replace("zzz", $"{totalMilliseconds:000}"); } t = t.Replace("hh", $"{timeCode.Hours:00}"); t = t.Replace("h", $"{timeCode.Hours}"); t = t.Replace("mm", $"{timeCode.Minutes:00}"); t = t.Replace("m", $"{timeCode.Minutes}"); t = t.Replace("ss", $"{timeCode.Seconds:00}"); t = t.Replace("s", $"{timeCode.Seconds}"); t = t.Replace("zzz", $"{timeCode.Milliseconds:000}"); t = t.Replace("zz", $"{Math.Round(timeCode.Milliseconds / 10.0):00}"); t = t.Replace("z", $"{Math.Round(timeCode.Milliseconds / 100.0):0}"); t = t.Replace("ff", $"{SubtitleFormat.MillisecondsToFramesMaxFrameRate(timeCode.Milliseconds):00}"); t = t.Replace("f", $"{SubtitleFormat.MillisecondsToFramesMaxFrameRate(timeCode.Milliseconds)}"); return(t); }
internal static string GetParagraph(string template, string start, string end, string text, string translation, int number, string actor, TimeCode duration, string timeCodeTemplate) { string d = duration.ToString(); if (timeCodeTemplate == "ff" || timeCodeTemplate == "f") { d = SubtitleFormat.MillisecondsToFrames(duration.TotalMilliseconds).ToString(CultureInfo.InvariantCulture); } if (timeCodeTemplate == "zzz" || timeCodeTemplate == "zz" || timeCodeTemplate == "z") { d = duration.TotalMilliseconds.ToString(CultureInfo.InvariantCulture); } if (timeCodeTemplate == "sss" || timeCodeTemplate == "ss" || timeCodeTemplate == "s") { d = duration.Seconds.ToString(CultureInfo.InvariantCulture); } else if (timeCodeTemplate.EndsWith("ss.ff", StringComparison.Ordinal)) { d = $"{duration.Seconds:00}.{SubtitleFormat.MillisecondsToFramesMaxFrameRate(duration.Milliseconds):00}"; } else if (timeCodeTemplate.EndsWith("ss:ff", StringComparison.Ordinal)) { d = $"{duration.Seconds:00}:{SubtitleFormat.MillisecondsToFramesMaxFrameRate(duration.Milliseconds):00}"; } else if (timeCodeTemplate.EndsWith("ss,ff", StringComparison.Ordinal)) { d = $"{duration.Seconds:00},{SubtitleFormat.MillisecondsToFramesMaxFrameRate(duration.Milliseconds):00}"; } else if (timeCodeTemplate.EndsWith("ss;ff", StringComparison.Ordinal)) { d = $"{duration.Seconds:00};{SubtitleFormat.MillisecondsToFramesMaxFrameRate(duration.Milliseconds):00}"; } else if (timeCodeTemplate.EndsWith("ss;ff", StringComparison.Ordinal)) { d = $"{duration.Seconds:00};{SubtitleFormat.MillisecondsToFramesMaxFrameRate(duration.Milliseconds):00}"; } else if (timeCodeTemplate.EndsWith("ss.zzz", StringComparison.Ordinal)) { d = $"{duration.Seconds:00}.{duration.Milliseconds:000}"; } else if (timeCodeTemplate.EndsWith("ss:zzz", StringComparison.Ordinal)) { d = $"{duration.Seconds:00}:{duration.Milliseconds:000}"; } else if (timeCodeTemplate.EndsWith("ss,zzz", StringComparison.Ordinal)) { d = $"{duration.Seconds:00},{duration.Milliseconds:000}"; } else if (timeCodeTemplate.EndsWith("ss;zzz", StringComparison.Ordinal)) { d = $"{duration.Seconds:00};{duration.Milliseconds:000}"; } else if (timeCodeTemplate.EndsWith("ss;zzz", StringComparison.Ordinal)) { d = $"{duration.Seconds:00};{duration.Milliseconds:000}"; } else if (timeCodeTemplate.EndsWith("ss.zz", StringComparison.Ordinal)) { d = $"{duration.Seconds:00}.{Math.Round(duration.Milliseconds / 10.0):00}"; } else if (timeCodeTemplate.EndsWith("ss:zz", StringComparison.Ordinal)) { d = $"{duration.Seconds:00}:{Math.Round(duration.Milliseconds / 10.0):00}"; } else if (timeCodeTemplate.EndsWith("ss,zz", StringComparison.Ordinal)) { d = $"{duration.Seconds:00},{Math.Round(duration.Milliseconds / 10.0):00}"; } else if (timeCodeTemplate.EndsWith("ss;zz", StringComparison.Ordinal)) { d = $"{duration.Seconds:00};{Math.Round(duration.Milliseconds / 10.0):00}"; } string s = template; s = s.Replace("{{", "@@@@_@@@{"); s = s.Replace("}}", "}@@@_@@@@"); s = string.Format(s, start, end, text, translation, number + 1, number, d, actor); s = s.Replace("@@@@_@@@", "{"); s = s.Replace("@@@_@@@@", "}"); return(s); }
public static string GetTimeCode(TimeCode timeCode, string template) { if (template.Trim() == "ss") { template = template.Replace("ss", string.Format("{0:00}", timeCode.TotalSeconds)); } if (template.Trim() == "s") { template = template.Replace("s", string.Format("{0}", timeCode.TotalSeconds)); } if (template.Trim() == "zzz") { template = template.Replace("zzz", string.Format("{0:000}", timeCode.TotalMilliseconds)); } if (template.Trim() == "z") { template = template.Replace("z", string.Format("{0}", timeCode.TotalMilliseconds)); } if (template.Trim() == "ff") { template = template.Replace("ff", string.Format("{0}", SubtitleFormat.MillisecondsToFrames(timeCode.TotalMilliseconds))); } if (template.StartsWith("ssssssss")) { template = template.Replace("ssssssss", string.Format("{0:00000000}", timeCode.TotalSeconds)); } if (template.StartsWith("sssssss")) { template = template.Replace("sssssss", string.Format("{0:0000000}", timeCode.TotalSeconds)); } if (template.StartsWith("ssssss")) { template = template.Replace("ssssss", string.Format("{0:000000}", timeCode.TotalSeconds)); } if (template.StartsWith("sssss")) { template = template.Replace("sssss", string.Format("{0:00000}", timeCode.TotalSeconds)); } if (template.StartsWith("ssss")) { template = template.Replace("ssss", string.Format("{0:0000}", timeCode.TotalSeconds)); } if (template.StartsWith("sss")) { template = template.Replace("sss", string.Format("{0:000}", timeCode.TotalSeconds)); } if (template.StartsWith("ss")) { template = template.Replace("ss", string.Format("{0:00}", timeCode.TotalSeconds)); } if (template.StartsWith("zzzzzzzz")) { template = template.Replace("zzzzzzzz", string.Format("{0:00000000}", timeCode.TotalMilliseconds)); } if (template.StartsWith("zzzzzzz")) { template = template.Replace("zzzzzzz", string.Format("{0:0000000}", timeCode.TotalMilliseconds)); } if (template.StartsWith("zzzzzz")) { template = template.Replace("zzzzzz", string.Format("{0:000000}", timeCode.TotalMilliseconds)); } if (template.StartsWith("zzzzz")) { template = template.Replace("zzzzz", string.Format("{0:00000}", timeCode.TotalMilliseconds)); } if (template.StartsWith("zzzz")) { template = template.Replace("zzzz", string.Format("{0:0000}", timeCode.TotalMilliseconds)); } if (template.StartsWith("zzz")) { template = template.Replace("zzz", string.Format("{0:000}", timeCode.TotalMilliseconds)); } template = template.Replace("hh", string.Format("{0:00}", timeCode.Hours)); template = template.Replace("h", string.Format("{0}", timeCode.Hours)); template = template.Replace("mm", string.Format("{0:00}", timeCode.Minutes)); template = template.Replace("m", string.Format("{0}", timeCode.Minutes)); template = template.Replace("ss", string.Format("{0:00}", timeCode.Seconds)); template = template.Replace("s", string.Format("{0}", timeCode.Seconds)); template = template.Replace("zzz", string.Format("{0:000}", timeCode.Milliseconds)); template = template.Replace("zz", string.Format("{0:00}", Math.Round(timeCode.Milliseconds / 10.0))); template = template.Replace("z", string.Format("{0:0}", Math.Round(timeCode.Milliseconds / 100.0))); template = template.Replace("ff", string.Format("{0:00}", SubtitleFormat.MillisecondsToFramesMaxFrameRate(timeCode.Milliseconds))); template = template.Replace("f", string.Format("{0}", SubtitleFormat.MillisecondsToFramesMaxFrameRate(timeCode.Milliseconds))); return(template); }
internal static string GetParagraph(string template, string start, string end, string text, string translation, int number, TimeCode duration, string timeCodeTemplate) { string d = duration.ToString(); if (timeCodeTemplate == "ff" || timeCodeTemplate == "f") { d = SubtitleFormat.MillisecondsToFrames(duration.TotalMilliseconds).ToString(); } if (timeCodeTemplate == "zzz" || timeCodeTemplate == "zz" || timeCodeTemplate == "z") { d = duration.TotalMilliseconds.ToString(); } if (timeCodeTemplate == "sss" || timeCodeTemplate == "ss" || timeCodeTemplate == "s") { d = duration.Seconds.ToString(); } else if (timeCodeTemplate.EndsWith("ss.ff")) { d = string.Format("{0:00}.{1:00}", duration.Seconds, SubtitleFormat.MillisecondsToFramesMaxFrameRate(duration.Milliseconds)); } else if (timeCodeTemplate.EndsWith("ss:ff")) { d = string.Format("{0:00}:{1:00}", duration.Seconds, SubtitleFormat.MillisecondsToFramesMaxFrameRate(duration.Milliseconds)); } else if (timeCodeTemplate.EndsWith("ss,ff")) { d = string.Format("{0:00},{1:00}", duration.Seconds, SubtitleFormat.MillisecondsToFramesMaxFrameRate(duration.Milliseconds)); } else if (timeCodeTemplate.EndsWith("ss;ff")) { d = string.Format("{0:00};{1:00}", duration.Seconds, SubtitleFormat.MillisecondsToFramesMaxFrameRate(duration.Milliseconds)); } else if (timeCodeTemplate.EndsWith("ss;ff")) { d = string.Format("{0:00};{1:00}", duration.Seconds, SubtitleFormat.MillisecondsToFramesMaxFrameRate(duration.Milliseconds)); } else if (timeCodeTemplate.EndsWith("ss.zzz")) { d = string.Format("{0:00}.{1:000}", duration.Seconds, duration.Milliseconds); } else if (timeCodeTemplate.EndsWith("ss:zzz")) { d = string.Format("{0:00}:{1:000}", duration.Seconds, duration.Milliseconds); } else if (timeCodeTemplate.EndsWith("ss,zzz")) { d = string.Format("{0:00},{1:000}", duration.Seconds, duration.Milliseconds); } else if (timeCodeTemplate.EndsWith("ss;zzz")) { d = string.Format("{0:00};{1:000}", duration.Seconds, duration.Milliseconds); } else if (timeCodeTemplate.EndsWith("ss;zzz")) { d = string.Format("{0:00};{1:000}", duration.Seconds, duration.Milliseconds); } else if (timeCodeTemplate.EndsWith("ss.zz")) { d = string.Format("{0:00}.{1:00}", duration.Seconds, Math.Round(duration.Milliseconds / 10.0)); } else if (timeCodeTemplate.EndsWith("ss:zz")) { d = string.Format("{0:00}:{1:00}", duration.Seconds, Math.Round(duration.Milliseconds / 10.0)); } else if (timeCodeTemplate.EndsWith("ss,zz")) { d = string.Format("{0:00},{1:00}", duration.Seconds, Math.Round(duration.Milliseconds / 10.0)); } else if (timeCodeTemplate.EndsWith("ss;zz")) { d = string.Format("{0:00};{1:00}", duration.Seconds, Math.Round(duration.Milliseconds / 10.0)); } string s = template; s = s.Replace("{{", "@@@@_@@@{"); s = s.Replace("}}", "}@@@_@@@@"); s = string.Format(s, start, end, text, translation, number + 1, number, d); s = s.Replace("@@@@_@@@", "{"); s = s.Replace("@@@_@@@@", "}"); return(s); }
/// <summary> /// Check the newly-updated timing to Shot Changes rules. /// https://partnerhelp.netflixstudios.com/hc/en-us/articles/360051554394-Timed-Text-Style-Guide-Subtitle-Timing-Guidelines /// </summary> public void Check(Subtitle subtitle, NetflixQualityController controller) { if (!controller.VideoExists) { return; } var SceneChanges = SceneChangeHelper.FromDisk(controller.VideoFileName); if (SceneChanges == null || SceneChanges.Count == 0) { return; } const int twelveFramesGap = 12; double twoFramesGap = 1000.0 / controller.FrameRate * 2.0; foreach (Paragraph p in subtitle.Paragraphs) { var fixedParagraph = new Paragraph(p, false); string comment = string.Empty; List <double> previousStartSceneChanges = SceneChanges.Where(x => SubtitleFormat.MillisecondsToFrames(x * 1000) < SubtitleFormat.MillisecondsToFrames(p.StartTime.TotalMilliseconds)).ToList(); List <double> nextStartSceneChanges = SceneChanges.Where(x => SubtitleFormat.MillisecondsToFrames(x * 1000) > SubtitleFormat.MillisecondsToFrames(p.StartTime.TotalMilliseconds)).ToList(); List <double> previousEndSceneChanges = SceneChanges.Where(x => SubtitleFormat.MillisecondsToFrames(x * 1000) < SubtitleFormat.MillisecondsToFrames(p.EndTime.TotalMilliseconds)).ToList(); List <double> nextEndSceneChanges = SceneChanges.Where(x => SubtitleFormat.MillisecondsToFrames(x * 1000) > SubtitleFormat.MillisecondsToFrames(p.EndTime.TotalMilliseconds)).ToList(); var onSceneChange = SceneChanges.Where(x => SubtitleFormat.MillisecondsToFrames(x * 1000) == SubtitleFormat.MillisecondsToFrames(p.EndTime.TotalMilliseconds)).FirstOrDefault(); if (previousStartSceneChanges.Count > 0) { double nearestStartPrevSceneChange = previousStartSceneChanges.Aggregate((x, y) => Math.Abs(x - p.StartTime.TotalSeconds) < Math.Abs(y - p.StartTime.TotalSeconds) ? x : y); if (SubtitleFormat.MillisecondsToFrames(p.StartTime.TotalMilliseconds - nearestStartPrevSceneChange * 1000) < twelveFramesGap) { fixedParagraph.StartTime.TotalMilliseconds = nearestStartPrevSceneChange * 1000; comment = "The in-cue is within 12 frames after the shot change"; controller.AddRecord(p, fixedParagraph, comment); } } if (nextStartSceneChanges.Count > 0) { double nearestStartNextSceneChange = nextStartSceneChanges.Aggregate((x, y) => Math.Abs(x - p.StartTime.TotalSeconds) < Math.Abs(y - p.StartTime.TotalSeconds) ? x : y); if (SubtitleFormat.MillisecondsToFrames(nearestStartNextSceneChange * 1000 - p.StartTime.TotalMilliseconds) < twelveFramesGap) { fixedParagraph.StartTime.TotalMilliseconds = nearestStartNextSceneChange * 1000; comment = "The in-cue is within 12 frames before the shot change"; controller.AddRecord(p, fixedParagraph, comment); } } if (previousEndSceneChanges.Count > 0) { double nearestEndPrevSceneChange = previousEndSceneChanges.Aggregate((x, y) => Math.Abs(x - p.EndTime.TotalSeconds) < Math.Abs(y - p.EndTime.TotalSeconds) ? x : y); if (SubtitleFormat.MillisecondsToFrames(p.EndTime.TotalMilliseconds - nearestEndPrevSceneChange * 1000) < twelveFramesGap) { fixedParagraph.EndTime.TotalMilliseconds = nearestEndPrevSceneChange * 1000 - twoFramesGap; comment = "The out-cue is within 12 frames after the shot change"; controller.AddRecord(p, fixedParagraph, comment); } } if (nextEndSceneChanges.Count > 0) { double nearestEndNextSceneChange = nextEndSceneChanges.Aggregate((x, y) => Math.Abs(x - p.EndTime.TotalSeconds) < Math.Abs(y - p.EndTime.TotalSeconds) ? x : y); if (SubtitleFormat.MillisecondsToFrames(nearestEndNextSceneChange * 1000 - p.EndTime.TotalMilliseconds) - 1 < twelveFramesGap && SubtitleFormat.MillisecondsToFrames(p.EndTime.TotalMilliseconds) != SubtitleFormat.MillisecondsToFrames(nearestEndNextSceneChange * 1000 - twoFramesGap)) { fixedParagraph.EndTime.TotalMilliseconds = nearestEndNextSceneChange * 1000 - twoFramesGap; comment = "The out-cue is within 12 frames of the last frame before the shot change"; controller.AddRecord(p, fixedParagraph, comment); } } if (onSceneChange > 0) { fixedParagraph.EndTime.TotalMilliseconds = onSceneChange * 1000 - twoFramesGap; comment = "The out-cue is on the shot change (respect the two-frame gap)"; controller.AddRecord(p, fixedParagraph, comment); } } }
public void Check(Subtitle subtitle, NetflixQualityController controller) { if (!controller.VideoExists) { return; } var SceneChanges = SceneChangeHelper.FromDisk(controller.VideoFileName); if (SceneChanges == null || SceneChanges.Count == 0) { return; } int halfSecGapInFrames = (int)Math.Round(controller.FrameRate / 2); double twoFramesGap = 1000.0 / controller.FrameRate * 2.0; foreach (Paragraph p in subtitle.Paragraphs) { var fixedParagraph = new Paragraph(p, false); string comment = string.Empty; List <double> previousStartSceneChanges = SceneChanges.Where(x => SubtitleFormat.MillisecondsToFrames(x * 1000) < SubtitleFormat.MillisecondsToFrames(p.StartTime.TotalMilliseconds)).ToList(); List <double> nextStartSceneChanges = SceneChanges.Where(x => SubtitleFormat.MillisecondsToFrames(x * 1000) > SubtitleFormat.MillisecondsToFrames(p.StartTime.TotalMilliseconds)).ToList(); List <double> previousEndSceneChanges = SceneChanges.Where(x => SubtitleFormat.MillisecondsToFrames(x * 1000) < SubtitleFormat.MillisecondsToFrames(p.EndTime.TotalMilliseconds)).ToList(); List <double> nextEndSceneChanges = SceneChanges.Where(x => SubtitleFormat.MillisecondsToFrames(x * 1000) > SubtitleFormat.MillisecondsToFrames(p.EndTime.TotalMilliseconds)).ToList(); var onSceneChange = SceneChanges.Where(x => SubtitleFormat.MillisecondsToFrames(x * 1000) == SubtitleFormat.MillisecondsToFrames(p.EndTime.TotalMilliseconds)).FirstOrDefault(); if (previousStartSceneChanges.Count > 0) { double nearestStartPrevSceneChange = previousStartSceneChanges.Aggregate((x, y) => Math.Abs(x - p.StartTime.TotalSeconds) < Math.Abs(y - p.StartTime.TotalSeconds) ? x : y); if (SubtitleFormat.MillisecondsToFrames(p.StartTime.TotalMilliseconds - nearestStartPrevSceneChange * 1000) < halfSecGapInFrames) { fixedParagraph.StartTime.TotalMilliseconds = nearestStartPrevSceneChange * 1000; comment = $"The in-cue is within {halfSecGapInFrames} frames after the shot change, snap the in-cue to the shot-change"; controller.AddRecord(p, fixedParagraph, comment); } } if (nextStartSceneChanges.Count > 0) { double nearestStartNextSceneChange = nextStartSceneChanges.Aggregate((x, y) => Math.Abs(x - p.StartTime.TotalSeconds) < Math.Abs(y - p.StartTime.TotalSeconds) ? x : y); var gapToSceneChange = SubtitleFormat.MillisecondsToFrames(nearestStartNextSceneChange * 1000 - p.StartTime.TotalMilliseconds); var threshold = (int)Math.Round(halfSecGapInFrames * 0.75); if (gapToSceneChange < halfSecGapInFrames) { if (gapToSceneChange < threshold) { fixedParagraph.StartTime.TotalMilliseconds = nearestStartNextSceneChange * 1000; comment = $"The in-cue is 1-{threshold - 1} frames before the shot change, snap the in-cue to the shot change"; } else { fixedParagraph.StartTime.TotalMilliseconds = nearestStartNextSceneChange * 1000 - (1000.0 / controller.FrameRate * halfSecGapInFrames); comment = $"The in-cue is {threshold}-{halfSecGapInFrames - 1} frames before the shot change, pull the in-cue to half a second ({halfSecGapInFrames} frames) before the shot-change"; } controller.AddRecord(p, fixedParagraph, comment); } } if (previousEndSceneChanges.Count > 0) { double nearestEndPrevSceneChange = previousEndSceneChanges.Aggregate((x, y) => Math.Abs(x - p.EndTime.TotalSeconds) < Math.Abs(y - p.EndTime.TotalSeconds) ? x : y); if (SubtitleFormat.MillisecondsToFrames(p.EndTime.TotalMilliseconds - nearestEndPrevSceneChange * 1000) < halfSecGapInFrames) { fixedParagraph.EndTime.TotalMilliseconds = nearestEndPrevSceneChange * 1000 - twoFramesGap; comment = $"The out-cue is within {halfSecGapInFrames} frames after the shot change"; controller.AddRecord(p, fixedParagraph, comment); } } if (nextEndSceneChanges.Count > 0) { double nearestEndNextSceneChange = nextEndSceneChanges.Aggregate((x, y) => Math.Abs(x - p.EndTime.TotalSeconds) < Math.Abs(y - p.EndTime.TotalSeconds) ? x : y); if (SubtitleFormat.MillisecondsToFrames(nearestEndNextSceneChange * 1000 - p.EndTime.TotalMilliseconds) - 1 < halfSecGapInFrames && SubtitleFormat.MillisecondsToFrames(p.EndTime.TotalMilliseconds) != SubtitleFormat.MillisecondsToFrames(nearestEndNextSceneChange * 1000 - twoFramesGap)) { fixedParagraph.EndTime.TotalMilliseconds = nearestEndNextSceneChange * 1000 - twoFramesGap; comment = $"The out-cue is within {halfSecGapInFrames} frames of the last frame before the shot change"; controller.AddRecord(p, fixedParagraph, comment); } } if (onSceneChange > 0) { fixedParagraph.EndTime.TotalMilliseconds = onSceneChange * 1000 - twoFramesGap; comment = "The out-cue is on the shot change, respect the two-frame gap"; controller.AddRecord(p, fixedParagraph, comment); } } }
public void MillisecondsToFrames2() { var frames = SubtitleFormat.MillisecondsToFrames(499, 25); Assert.AreEqual(12, frames); }
public void MillisecondsToFrames1() { var frames = SubtitleFormat.MillisecondsToFrames(500, 25); Assert.AreEqual(13, frames); }
public string ToHHMMSSPeriodFF() { return(string.Format("{0:00}:{1:00}:{2:00}.{3:00}", _time.Hours, _time.Minutes, _time.Seconds, SubtitleFormat.MillisecondsToFrames(_time.Milliseconds))); }