diff --git a/src/libse/Common/Settings.cs b/src/libse/Common/Settings.cs index 4073165d3..bddb7e2af 100644 --- a/src/libse/Common/Settings.cs +++ b/src/libse/Common/Settings.cs @@ -333,6 +333,7 @@ namespace Nikse.SubtitleEdit.Core.Common public Color BlankVideoColor { get; set; } public bool BlankVideoUseCheckeredImage { get; set; } public int BlankVideoMinutes { get; set; } + public decimal BlankVideoFrameRate { get; set; } public ToolsSettings() { @@ -475,6 +476,7 @@ namespace Nikse.SubtitleEdit.Core.Common BlankVideoColor = Color.CadetBlue; BlankVideoUseCheckeredImage = true; BlankVideoMinutes = 2; + BlankVideoFrameRate = 23.976m; } } @@ -5111,7 +5113,13 @@ $HorzAlign = Center subNode = node.SelectSingleNode("BlankVideoMinutes"); if (subNode != null) { - settings.Tools.BlankVideoMinutes = Convert.ToInt32(subNode.InnerText); + settings.Tools.BlankVideoMinutes = Convert.ToInt32(subNode.InnerText, CultureInfo.InvariantCulture); + } + + subNode = node.SelectSingleNode("BlankVideoFrameRate"); + if (subNode != null) + { + settings.Tools.BlankVideoFrameRate = Convert.ToDecimal(subNode.InnerText, CultureInfo.InvariantCulture); } subNode = node.SelectSingleNode("BlankVideoUseCheckeredImage"); @@ -8754,6 +8762,7 @@ $HorzAlign = Center textWriter.WriteElementString("BlankVideoColor", ColorTranslator.ToHtml(settings.Tools.BlankVideoColor)); textWriter.WriteElementString("BlankVideoUseCheckeredImage", settings.Tools.BlankVideoUseCheckeredImage.ToString(CultureInfo.InvariantCulture)); textWriter.WriteElementString("BlankVideoMinutes", settings.Tools.BlankVideoMinutes.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("BlankVideoFrameRate", settings.Tools.BlankVideoFrameRate.ToString(CultureInfo.InvariantCulture)); if (settings.Tools.FindHistory != null && settings.Tools.FindHistory.Count > 0) { diff --git a/src/ui/Forms/GenerateVideo.Designer.cs b/src/ui/Forms/GenerateVideo.Designer.cs index 7cafbcb0f..02e83545b 100644 --- a/src/ui/Forms/GenerateVideo.Designer.cs +++ b/src/ui/Forms/GenerateVideo.Designer.cs @@ -44,6 +44,8 @@ namespace Nikse.SubtitleEdit.Forms this.radioButtonCheckeredImage = new System.Windows.Forms.RadioButton(); this.numericUpDownDurationMinutes = new System.Windows.Forms.NumericUpDown(); this.labelPleaseWait = new System.Windows.Forms.Label(); + this.labelFrameRate = new System.Windows.Forms.Label(); + this.comboBoxFrameRate = new System.Windows.Forms.ComboBox(); ((System.ComponentModel.ISupportInitialize)(this.numericUpDownWidth)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.numericUpDownHeight)).BeginInit(); this.groupBoxBackground.SuspendLayout(); @@ -56,7 +58,7 @@ namespace Nikse.SubtitleEdit.Forms this.panelColor.Location = new System.Drawing.Point(169, 81); this.panelColor.Name = "panelColor"; this.panelColor.Size = new System.Drawing.Size(21, 20); - this.panelColor.TabIndex = 3; + this.panelColor.TabIndex = 11; this.panelColor.MouseClick += new System.Windows.Forms.MouseEventHandler(this.panelColor_MouseClick); // // buttonColor @@ -64,7 +66,7 @@ namespace Nikse.SubtitleEdit.Forms this.buttonColor.Location = new System.Drawing.Point(42, 80); this.buttonColor.Name = "buttonColor"; this.buttonColor.Size = new System.Drawing.Size(121, 23); - this.buttonColor.TabIndex = 2; + this.buttonColor.TabIndex = 10; this.buttonColor.Text = "Color"; this.buttonColor.UseVisualStyleBackColor = true; this.buttonColor.Click += new System.EventHandler(this.buttonColor_Click); @@ -76,7 +78,7 @@ namespace Nikse.SubtitleEdit.Forms this.buttonOK.Location = new System.Drawing.Point(451, 155); this.buttonOK.Name = "buttonOK"; this.buttonOK.Size = new System.Drawing.Size(121, 23); - this.buttonOK.TabIndex = 9; + this.buttonOK.TabIndex = 20; this.buttonOK.Text = "Generate"; this.buttonOK.UseVisualStyleBackColor = true; this.buttonOK.Click += new System.EventHandler(this.buttonOK_Click); @@ -89,7 +91,7 @@ namespace Nikse.SubtitleEdit.Forms this.buttonCancel.Location = new System.Drawing.Point(578, 155); this.buttonCancel.Name = "buttonCancel"; this.buttonCancel.Size = new System.Drawing.Size(75, 23); - this.buttonCancel.TabIndex = 10; + this.buttonCancel.TabIndex = 21; this.buttonCancel.Text = "C&ancel"; this.buttonCancel.UseVisualStyleBackColor = true; this.buttonCancel.Click += new System.EventHandler(this.buttonCancel_Click); @@ -184,7 +186,7 @@ namespace Nikse.SubtitleEdit.Forms this.groupBoxBackground.Location = new System.Drawing.Point(339, 12); this.groupBoxBackground.Name = "groupBoxBackground"; this.groupBoxBackground.Size = new System.Drawing.Size(314, 118); - this.groupBoxBackground.TabIndex = 6; + this.groupBoxBackground.TabIndex = 7; this.groupBoxBackground.TabStop = false; this.groupBoxBackground.Text = "Background"; // @@ -194,7 +196,7 @@ namespace Nikse.SubtitleEdit.Forms this.radioButtonColor.Location = new System.Drawing.Point(18, 57); this.radioButtonColor.Name = "radioButtonColor"; this.radioButtonColor.Size = new System.Drawing.Size(74, 17); - this.radioButtonColor.TabIndex = 1; + this.radioButtonColor.TabIndex = 9; this.radioButtonColor.Text = "Solid color"; this.radioButtonColor.UseVisualStyleBackColor = true; // @@ -205,7 +207,7 @@ namespace Nikse.SubtitleEdit.Forms this.radioButtonCheckeredImage.Location = new System.Drawing.Point(18, 34); this.radioButtonCheckeredImage.Name = "radioButtonCheckeredImage"; this.radioButtonCheckeredImage.Size = new System.Drawing.Size(108, 17); - this.radioButtonCheckeredImage.TabIndex = 0; + this.radioButtonCheckeredImage.TabIndex = 8; this.radioButtonCheckeredImage.TabStop = true; this.radioButtonCheckeredImage.Text = "Checkered image"; this.radioButtonCheckeredImage.UseVisualStyleBackColor = true; @@ -242,11 +244,31 @@ namespace Nikse.SubtitleEdit.Forms this.labelPleaseWait.TabIndex = 7; this.labelPleaseWait.Text = "Please wait..."; // + // labelFrameRate + // + this.labelFrameRate.AutoSize = true; + this.labelFrameRate.Location = new System.Drawing.Point(12, 83); + this.labelFrameRate.Name = "labelFrameRate"; + this.labelFrameRate.Size = new System.Drawing.Size(57, 13); + this.labelFrameRate.TabIndex = 11; + this.labelFrameRate.Text = "Frame rate"; + // + // comboBoxFrameRate + // + this.comboBoxFrameRate.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.comboBoxFrameRate.FormattingEnabled = true; + this.comboBoxFrameRate.Location = new System.Drawing.Point(165, 83); + this.comboBoxFrameRate.Name = "comboBoxFrameRate"; + this.comboBoxFrameRate.Size = new System.Drawing.Size(121, 21); + this.comboBoxFrameRate.TabIndex = 6; + // // GenerateVideo // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(665, 190); + this.Controls.Add(this.comboBoxFrameRate); + this.Controls.Add(this.labelFrameRate); this.Controls.Add(this.labelPleaseWait); this.Controls.Add(this.numericUpDownDurationMinutes); this.Controls.Add(this.groupBoxBackground); @@ -293,5 +315,7 @@ namespace Nikse.SubtitleEdit.Forms private System.Windows.Forms.RadioButton radioButtonCheckeredImage; private System.Windows.Forms.NumericUpDown numericUpDownDurationMinutes; private System.Windows.Forms.Label labelPleaseWait; + private System.Windows.Forms.Label labelFrameRate; + private System.Windows.Forms.ComboBox comboBoxFrameRate; } } \ No newline at end of file diff --git a/src/ui/Forms/GenerateVideo.cs b/src/ui/Forms/GenerateVideo.cs index a070e06bf..f061616f0 100644 --- a/src/ui/Forms/GenerateVideo.cs +++ b/src/ui/Forms/GenerateVideo.cs @@ -1,6 +1,7 @@ using Nikse.SubtitleEdit.Core.Common; using Nikse.SubtitleEdit.Logic; using System; +using System.Globalization; using System.IO; using System.Windows.Forms; @@ -33,6 +34,7 @@ namespace Nikse.SubtitleEdit.Forms radioButtonCheckeredImage.Text = LanguageSettings.Current.GenerateBlankVideo.CheckeredImage; radioButtonColor.Text = LanguageSettings.Current.GenerateBlankVideo.SolidColor; labelDuration.Text = LanguageSettings.Current.GenerateBlankVideo.DurationInMinutes; + labelFrameRate.Text = LanguageSettings.Current.General.FrameRate; buttonColor.Text = LanguageSettings.Current.Settings.ChooseColor; buttonOK.Text = LanguageSettings.Current.Watermark.Generate; labelResolution.Text = LanguageSettings.Current.ExportPngXml.VideoResolution; @@ -46,6 +48,28 @@ namespace Nikse.SubtitleEdit.Forms numericUpDownWidth.Left = left; labelX.Left = numericUpDownWidth.Left + numericUpDownWidth.Width + 3; numericUpDownHeight.Left = labelX.Left + labelX.Width + 3; + comboBoxFrameRate.Left = left; + + comboBoxFrameRate.Items.Clear(); + comboBoxFrameRate.Items.Add(23.976.ToString(CultureInfo.CurrentCulture)); + comboBoxFrameRate.Items.Add(24.0.ToString(CultureInfo.CurrentCulture)); + comboBoxFrameRate.Items.Add(25.0.ToString(CultureInfo.CurrentCulture)); + comboBoxFrameRate.Items.Add(29.97.ToString(CultureInfo.CurrentCulture)); + comboBoxFrameRate.Items.Add(30.00.ToString(CultureInfo.CurrentCulture)); + comboBoxFrameRate.Items.Add(50.00.ToString(CultureInfo.CurrentCulture)); + comboBoxFrameRate.Items.Add(59.94.ToString(CultureInfo.CurrentCulture)); + comboBoxFrameRate.Items.Add(60.00.ToString(CultureInfo.CurrentCulture)); + comboBoxFrameRate.SelectedIndex = 0; + for (var index = 0; index < comboBoxFrameRate.Items.Count; index++) + { + var item = comboBoxFrameRate.Items[index]; + var v = Convert.ToDecimal(item.ToString(), CultureInfo.CurrentCulture); + if (Math.Abs(v - Configuration.Settings.Tools.BlankVideoFrameRate) < 0.01m) + { + comboBoxFrameRate.SelectedIndex = index; + break; + } + } } private void buttonOK_Click(object sender, EventArgs e) @@ -76,7 +100,9 @@ namespace Nikse.SubtitleEdit.Forms (int)numericUpDownWidth.Value, (int)numericUpDownHeight.Value, panelColor.BackColor, - radioButtonCheckeredImage.Checked); + radioButtonCheckeredImage.Checked, + decimal.Parse(comboBoxFrameRate.Text) + ); process.Start(); while (!process.HasExited) @@ -115,6 +141,7 @@ namespace Nikse.SubtitleEdit.Forms Configuration.Settings.Tools.BlankVideoColor = panelColor.BackColor; Configuration.Settings.Tools.BlankVideoUseCheckeredImage = radioButtonCheckeredImage.Checked; Configuration.Settings.Tools.BlankVideoMinutes = (int)numericUpDownDurationMinutes.Value; + Configuration.Settings.Tools.BlankVideoFrameRate = Convert.ToDecimal(comboBoxFrameRate.Text, CultureInfo.CurrentCulture); } private void buttonCancel_Click(object sender, EventArgs e) diff --git a/src/ui/Logic/VideoPreviewGenerator.cs b/src/ui/Logic/VideoPreviewGenerator.cs index 0da8f3aef..80fe10dcb 100644 --- a/src/ui/Logic/VideoPreviewGenerator.cs +++ b/src/ui/Logic/VideoPreviewGenerator.cs @@ -1,9 +1,10 @@ -using System; +using Nikse.SubtitleEdit.Core.Common; +using System; using System.Diagnostics; using System.Drawing; using System.Drawing.Imaging; +using System.Globalization; using System.IO; -using Nikse.SubtitleEdit.Core.Common; namespace Nikse.SubtitleEdit.Logic { @@ -19,7 +20,7 @@ namespace Nikse.SubtitleEdit.Logic try { - var process = GenerateVideoFile(previewFileName, 3, 720, 480, Color.Black, true); + var process = GenerateVideoFile(previewFileName, 3, 720, 480, Color.Black, true, 25); process.Start(); process.WaitForExit(); @@ -31,7 +32,7 @@ namespace Nikse.SubtitleEdit.Logic } } - public static Process GenerateVideoFile(string previewFileName, int seconds, int width, int height, Color color, bool checkered) + public static Process GenerateVideoFile(string previewFileName, int seconds, int width, int height, Color color, bool checkered, decimal frameRate) { Process processMakeVideo; @@ -41,17 +42,17 @@ namespace Nikse.SubtitleEdit.Logic var backgroundImage = TextDesigner.MakeBackgroundImage(width, height, rectangleSize, Configuration.Settings.General.UseDarkTheme); var tempImageFileName = Path.Combine(Path.GetTempPath(), Guid.NewGuid() + ".png"); backgroundImage.Save(tempImageFileName, ImageFormat.Png); - processMakeVideo = GetFFmpegProcess(tempImageFileName, previewFileName, backgroundImage.Width, backgroundImage.Height, seconds); + processMakeVideo = GetFFmpegProcess(tempImageFileName, previewFileName, backgroundImage.Width, backgroundImage.Height, seconds, frameRate); } else { - processMakeVideo = GetFFmpegProcess(color, previewFileName, width, height, seconds); + processMakeVideo = GetFFmpegProcess(color, previewFileName, width, height, seconds, frameRate); } return processMakeVideo; } - private static Process GetFFmpegProcess(string imageFileName, string outputFileName, int videoWidth, int videoHeight, int seconds) + private static Process GetFFmpegProcess(string imageFileName, string outputFileName, int videoWidth, int videoHeight, int seconds, decimal frameRate) { var ffmpegLocation = Configuration.Settings.General.FFmpegLocation; if (!Configuration.IsRunningOnWindows && (string.IsNullOrEmpty(ffmpegLocation) || !File.Exists(ffmpegLocation))) @@ -64,14 +65,14 @@ namespace Nikse.SubtitleEdit.Logic StartInfo = { FileName = ffmpegLocation, - Arguments = $"-t {seconds} -loop 1 -i \"{imageFileName}\" -c:v libx264 -tune stillimage -shortest -s {videoWidth}x{videoHeight} \"{outputFileName}\"", + Arguments = $"-t {seconds} -loop 1 -r {frameRate.ToString(CultureInfo.InvariantCulture)} -i \"{imageFileName}\" -c:v libx264 -tune stillimage -shortest -s {videoWidth}x{videoHeight} \"{outputFileName}\"", UseShellExecute = false, CreateNoWindow = true } }; } - private static Process GetFFmpegProcess(Color color, string outputFileName, int videoWidth, int videoHeight, int seconds) + private static Process GetFFmpegProcess(Color color, string outputFileName, int videoWidth, int videoHeight, int seconds, decimal frameRate) { var ffmpegLocation = Configuration.Settings.General.FFmpegLocation; if (!Configuration.IsRunningOnWindows && (string.IsNullOrEmpty(ffmpegLocation) || !File.Exists(ffmpegLocation))) @@ -86,7 +87,7 @@ namespace Nikse.SubtitleEdit.Logic StartInfo = { FileName = ffmpegLocation, - Arguments = $"-t {seconds} -f lavfi -i color=c={htmlColor} -c:v libx264 -tune stillimage -shortest -s {videoWidth}x{videoHeight} \"{outputFileName}\"", + Arguments = $"-t {seconds} -f lavfi -i color=c={htmlColor}:r={frameRate.ToString(CultureInfo.InvariantCulture)} -c:v libx264 -tune stillimage -shortest -s {videoWidth}x{videoHeight} \"{outputFileName}\"", UseShellExecute = false, CreateNoWindow = true }