private void GenerateShotChanges(string videoFileName) { var shotChangesGenerator = new ShotChangesGenerator(); var threshold = 0.4m; if (decimal.TryParse(Configuration.Settings.General.FFmpegSceneThreshold, NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out var d)) { threshold = d; } using (var process = shotChangesGenerator.GetProcess(videoFileName, threshold)) { while (!process.HasExited) { Application.DoEvents(); System.Threading.Thread.Sleep(100); if (_abort) { DialogResult = DialogResult.Cancel; process.Kill(); return; } } } var seconds = ShotChangesGenerator.GetSeconds(shotChangesGenerator.GetTimeCodesString().SplitToLines().ToArray()); if (seconds.Count > 0) { ShotChangeHelper.SaveShotChanges(videoFileName, seconds); } }
private void RefreshCheckBoxes(string language) { _netflixQualityController = new NetflixQualityController { Language = language, VideoFileName = _videoFileName, FrameRate = _frameRate }; checkBoxNoItalics.Checked = !_netflixQualityController.AllowItalics; checkBoxNoItalics.Enabled = !_netflixQualityController.AllowItalics; int halfSecGapInFrames = (int)Math.Round(_frameRate / 2, MidpointRounding.AwayFromZero); checkBoxGapBridge.Text = $"Frame gap: 3 to {halfSecGapInFrames - 1} frames => 2 frames"; var shotChangesExist = false; if (_netflixQualityController.VideoExists) { if (ShotChangeHelper.FromDisk(_videoFileName).Count > 0) { shotChangesExist = true; } } checkBoxShotChange.Checked = shotChangesExist; checkBoxShotChange.Enabled = shotChangesExist; var checkFrameRate = _subtitleFormat.GetType() == new NetflixTimedText().GetType(); checkBoxTtmlFrameRate.Checked = checkFrameRate; checkBoxTtmlFrameRate.Enabled = checkFrameRate; var speakerStyle = _netflixQualityController.SpeakerStyle; var checkBoxSpeakerStyleText = "Dual Speakers: Use a hyphen without a space"; if (speakerStyle == DialogType.DashBothLinesWithSpace) { checkBoxSpeakerStyleText = "Dual Speakers: Use a hyphen with a space"; } else if (speakerStyle == DialogType.DashSecondLineWithSpace) { checkBoxSpeakerStyleText = "Dual Speakers: Use a hyphen with a space to denote the second speaker only"; } else if (speakerStyle == DialogType.DashSecondLineWithoutSpace) { checkBoxSpeakerStyleText = "Dual Speakers: Use a hyphen without a space to denote the second speaker only"; } checkBoxSpeakerStyle.Text = checkBoxSpeakerStyleText; checkBox17CharsPerSecond.Text = string.Format(LanguageSettings.Current.NetflixQualityCheck.MaximumXCharsPerSecond, _netflixQualityController.CharactersPerSecond); checkBoxMaxLineLength.Text = string.Format(LanguageSettings.Current.NetflixQualityCheck.MaximumLineLength, _netflixQualityController.SingleLineMaxLength); }
public void Check(Subtitle subtitle, NetflixQualityController controller) { if (!controller.VideoExists) { return; } var shotChanges = ShotChangeHelper.FromDisk(controller.VideoFileName); if (shotChanges == null || shotChanges.Count == 0) { return; } if (Configuration.Settings.General.CurrentVideoIsSmpte) { shotChanges = shotChanges.Select(sc => sc /= 1.001).ToList(); } int halfSecGapInFrames = (int)Math.Round(controller.FrameRate / 2, MidpointRounding.AwayFromZero); 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> previousStartShotChanges = shotChanges.Where(x => SubtitleFormat.MillisecondsToFrames(x * 1000) < SubtitleFormat.MillisecondsToFrames(p.StartTime.TotalMilliseconds)).ToList(); List <double> nextStartShotChanges = shotChanges.Where(x => SubtitleFormat.MillisecondsToFrames(x * 1000) > SubtitleFormat.MillisecondsToFrames(p.StartTime.TotalMilliseconds)).ToList(); List <double> previousEndShotChanges = shotChanges.Where(x => SubtitleFormat.MillisecondsToFrames(x * 1000) < SubtitleFormat.MillisecondsToFrames(p.EndTime.TotalMilliseconds)).ToList(); List <double> nextEndShotChanges = shotChanges.Where(x => SubtitleFormat.MillisecondsToFrames(x * 1000) > SubtitleFormat.MillisecondsToFrames(p.EndTime.TotalMilliseconds)).ToList(); var onShotChange = shotChanges.FirstOrDefault(x => SubtitleFormat.MillisecondsToFrames(x * 1000) == SubtitleFormat.MillisecondsToFrames(p.EndTime.TotalMilliseconds)); if (previousStartShotChanges.Count > 0) { double nearestStartPrevShotChange = previousStartShotChanges.Aggregate((x, y) => Math.Abs(x - p.StartTime.TotalSeconds) < Math.Abs(y - p.StartTime.TotalSeconds) ? x : y); if (SubtitleFormat.MillisecondsToFrames(p.StartTime.TotalMilliseconds - nearestStartPrevShotChange * 1000) < halfSecGapInFrames) { fixedParagraph.StartTime.TotalMilliseconds = nearestStartPrevShotChange * 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 (nextStartShotChanges.Count > 0) { double nearestStartNextShotChange = nextStartShotChanges.Aggregate((x, y) => Math.Abs(x - p.StartTime.TotalSeconds) < Math.Abs(y - p.StartTime.TotalSeconds) ? x : y); var gapToShotChange = SubtitleFormat.MillisecondsToFrames(nearestStartNextShotChange * 1000 - p.StartTime.TotalMilliseconds); var threshold = (int)Math.Round(halfSecGapInFrames * 0.75, MidpointRounding.AwayFromZero); if (gapToShotChange != 0 && gapToShotChange < halfSecGapInFrames) { if (gapToShotChange < threshold) { fixedParagraph.StartTime.TotalMilliseconds = nearestStartNextShotChange * 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 = nearestStartNextShotChange * 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 (previousEndShotChanges.Count > 0) { double nearestEndPrevShotChange = previousEndShotChanges.Aggregate((x, y) => Math.Abs(x - p.EndTime.TotalSeconds) < Math.Abs(y - p.EndTime.TotalSeconds) ? x : y); if (SubtitleFormat.MillisecondsToFrames(p.EndTime.TotalMilliseconds - nearestEndPrevShotChange * 1000) < halfSecGapInFrames) { fixedParagraph.EndTime.TotalMilliseconds = nearestEndPrevShotChange * 1000 - twoFramesGap; comment = $"The out-cue is within {halfSecGapInFrames} frames after the shot change"; controller.AddRecord(p, fixedParagraph, comment); } } if (nextEndShotChanges.Count > 0) { double nearestEndNextShotChange = nextEndShotChanges.Aggregate((x, y) => Math.Abs(x - p.EndTime.TotalSeconds) < Math.Abs(y - p.EndTime.TotalSeconds) ? x : y); if (SubtitleFormat.MillisecondsToFrames(nearestEndNextShotChange * 1000 - p.EndTime.TotalMilliseconds) < halfSecGapInFrames && SubtitleFormat.MillisecondsToFrames(nearestEndNextShotChange * 1000 - p.EndTime.TotalMilliseconds) < 2) { fixedParagraph.EndTime.TotalMilliseconds = nearestEndNextShotChange * 1000 - twoFramesGap; comment = $"The out-cue is within {halfSecGapInFrames} frames of the shot change"; controller.AddRecord(p, fixedParagraph, comment); } } if (onShotChange > 0) { fixedParagraph.EndTime.TotalMilliseconds = onShotChange * 1000 - twoFramesGap; comment = "The out-cue is on the shot change, respect the two-frame gap"; controller.AddRecord(p, fixedParagraph, comment); } } }