Add some support for PGS in mkv for "embed subs"

This commit is contained in:
Nikolaj Olsson 2024-03-29 08:06:13 +01:00
parent d3cb4c8053
commit 78351ffc2b
5 changed files with 190 additions and 52 deletions

View File

@ -1029,6 +1029,7 @@ We leverage the intrinsic rhythm of the image.</CreateSimpleChainingToolTip>
<ToggleDefault>Toggle default</ToggleDefault>
<Default>Default</Default>
<XGeneratedWithEmbeddedSubs>"{0}" generated with embedded subtitles</XGeneratedWithEmbeddedSubs>
<DeleteInputVideo>Delete input video file after "Generate"</DeleteInputVideo>
</GenerateVideoWithEmbeddedSubs>
<GetDictionaries>
<Title>Need dictionaries?</Title>

View File

@ -26,6 +26,7 @@ namespace Nikse.SubtitleEdit.Forms
private string _inputVideoFileName;
private StringBuilder _log;
private readonly List<VideoPreviewGeneratorSub> _softSubs = new List<VideoPreviewGeneratorSub>();
private readonly List<VideoPreviewGeneratorSub> _tracksToDelete = new List<VideoPreviewGeneratorSub>();
private bool _promptFFmpegParameters;
private readonly List<string> _cleanUpFolders = new List<string>();
@ -132,7 +133,6 @@ namespace Nikse.SubtitleEdit.Forms
_inputVideoFileName = inputVideoFileName;
_videoInfo = videoInfo;
using (var matroska = new MatroskaFile(inputVideoFileName))
{
if (matroska.IsValid)
@ -159,6 +159,8 @@ namespace Nikse.SubtitleEdit.Forms
listViewSubtitles.EndUpdate();
}
_tracksToDelete.Clear();
}
private void AddListViewItem(Trak track)
@ -185,7 +187,7 @@ namespace Nikse.SubtitleEdit.Forms
var item = new ListViewItem
{
Tag = sub,
Text = sub.SubtitleFormat != null ? sub.SubtitleFormat.Name : sub.Format,
Text = sub.SubtitleFormat ?? sub.Format,
};
item.SubItems.Add(GetDisplayLanguage(sub.Language));
item.SubItems.Add(sub.IsDefault.ToString(CultureInfo.InvariantCulture));
@ -221,8 +223,24 @@ namespace Nikse.SubtitleEdit.Forms
private void AddListViewItem(MatroskaTrackInfo track, MatroskaFile matroska)
{
if (track.CodecId.Equals("S_HDMV/PGS", StringComparison.OrdinalIgnoreCase))
{
AddListViewItem(new VideoPreviewGeneratorSub
{
Name = track.CodecId,
Language = track.Language,
IsNew = false,
IsForced = track.IsForced,
IsDefault = track.IsDefault,
Tag = track,
SubtitleFormat = track.CodecId,
});
return;
}
if (track.CodecId.Equals("S_VOBSUB", StringComparison.OrdinalIgnoreCase) ||
track.CodecId.Equals("S_HDMV/PGS", StringComparison.OrdinalIgnoreCase) ||
track.CodecId.Equals("S_HDMV/TEXTST", StringComparison.OrdinalIgnoreCase) ||
track.CodecId.Equals("S_DVBSUB", StringComparison.OrdinalIgnoreCase))
{
@ -253,7 +271,7 @@ namespace Nikse.SubtitleEdit.Forms
IsForced = track.IsForced,
IsDefault = track.IsDefault,
Tag = track,
SubtitleFormat = format,
SubtitleFormat = format.GetType().Name,
FileName = fileName,
});
}
@ -268,19 +286,17 @@ namespace Nikse.SubtitleEdit.Forms
if (fileName.EndsWith(".sup", StringComparison.OrdinalIgnoreCase) &&
FileUtil.IsBluRaySup(fileName))
{
MessageBox.Show("FFmpeg does not support embedding of PGS/Blu-ray sup :(");
//AddListViewItem(new VideoPreviewGeneratorSub
//{
// Name = Path.GetFileName(fileName),
// Language = "eng", //TODO: get from file name or sup
// Format = "Blu-ray sup",
// SubtitleFormat = null,
// IsNew = true,
// IsForced = false,
// IsDefault = false,
// FileName = fileName,
//});
AddListViewItem(new VideoPreviewGeneratorSub
{
Name = Path.GetFileName(fileName),
Language = GetLanguageFromFileName(fileName),
Format = "Blu-ray sup",
SubtitleFormat = null,
IsNew = true,
IsForced = false,
IsDefault = false,
FileName = fileName,
});
return;
}
@ -301,7 +317,7 @@ namespace Nikse.SubtitleEdit.Forms
Name = Path.GetFileName(fileName),
Language = LanguageAutoDetect.AutoDetectGoogleLanguage(subtitle),
Format = subtitle.OriginalFormat.FriendlyName,
SubtitleFormat = subtitle.OriginalFormat,
SubtitleFormat = subtitle.OriginalFormat.GetType().Name,
IsNew = true,
IsForced = false,
IsDefault = false,
@ -309,6 +325,41 @@ namespace Nikse.SubtitleEdit.Forms
});
}
private static string GetLanguageFromFileName(string fileName)
{
var defaultLanguage = "eng";
if (string.IsNullOrEmpty(fileName))
{
return defaultLanguage;
}
var fileNameNoExt = Path.GetFileNameWithoutExtension(fileName);
var split = fileNameNoExt.Split('.', '_', '_');
var last = split.LastOrDefault();
if (last == null)
{
return defaultLanguage;
}
if (last.Length == 3)
{
return last;
}
if (last.Length == 2)
{
var threeLetterCode = Iso639Dash2LanguageCode.GetThreeLetterCodeFromTwoLetterCode(last);
if (threeLetterCode.Length == 3)
{
return threeLetterCode;
}
}
return defaultLanguage;
}
private string GetKnownFileNameOrConvertToSrtOrUtf8(string fileName, Subtitle subtitle)
{
SubtitleFormat targetFormat = new SubRip();
@ -390,6 +441,13 @@ namespace Nikse.SubtitleEdit.Forms
VideoFileName = saveDialog.FileName;
}
var isMp4 = VideoFileName.EndsWith(".mp4", StringComparison.OrdinalIgnoreCase);
if (isMp4 && _softSubs.Any(p => p.SubtitleFormat == "S_HDMV/PGS"))
{
MessageBox.Show("Cannot embed S_HDMV/PGS in MP4");
return;
}
if (File.Exists(VideoFileName))
{
try
@ -550,6 +608,7 @@ namespace Nikse.SubtitleEdit.Forms
return VideoPreviewGenerator.GenerateSoftCodedVideoFile(
inputVideoFileName,
_softSubs,
_tracksToDelete,
outputVideoFileName,
OutputHandler);
}
@ -756,6 +815,7 @@ namespace Nikse.SubtitleEdit.Forms
foreach (var index in list.OrderByDescending(p => p))
{
_tracksToDelete.Add(_softSubs[index]);
_softSubs.RemoveAt(index);
listViewSubtitles.Items.RemoveAt(index);
}
@ -780,6 +840,7 @@ namespace Nikse.SubtitleEdit.Forms
private void buttonClear_Click(object sender, EventArgs e)
{
listViewSubtitles.Items.Clear();
_tracksToDelete.AddRange(_softSubs);
_softSubs.Clear();
labelSubtitles.Text = string.Format(LanguageSettings.Current.GenerateVideoWithEmbeddedSubs.SubtitlesX, listViewSubtitles.Items.Count);
}

View File

@ -2716,6 +2716,9 @@ namespace Nikse.SubtitleEdit.Logic
case "GenerateVideoWithEmbeddedSubs/XGeneratedWithEmbeddedSubs":
language.GenerateVideoWithEmbeddedSubs.XGeneratedWithEmbeddedSubs = reader.Value;
break;
case "GenerateVideoWithEmbeddedSubs/DeleteInputVideo":
language.GenerateVideoWithEmbeddedSubs.DeleteInputVideo = reader.Value;
break;
case "GetDictionaries/Title":
language.GetDictionaries.Title = reader.Value;
break;

View File

@ -1,5 +1,4 @@
using Nikse.SubtitleEdit.Core.Common;
using Nikse.SubtitleEdit.Core.SubtitleFormats;
using Nikse.SubtitleEdit.Forms;
using System;
using System.Collections.Generic;
@ -9,6 +8,7 @@ using System.Drawing.Imaging;
using System.Globalization;
using System.IO;
using System.Linq;
using Nikse.SubtitleEdit.Core.ContainerFormats.Matroska;
namespace Nikse.SubtitleEdit.Logic
{
@ -355,18 +355,116 @@ namespace Nikse.SubtitleEdit.Logic
return ffmpegLocation;
}
public static Process GenerateSoftCodedVideoFile(string inputVideoFileName, List<VideoPreviewGeneratorSub> softSubs, string outputVideoFileName, DataReceivedEventHandler outputHandler)
public static Process GenerateSoftCodedVideoFile(string inputVideoFileName, List<VideoPreviewGeneratorSub> softSubs, List<VideoPreviewGeneratorSub> softSubsToDelete, string outputVideoFileName, DataReceivedEventHandler outputHandler)
{
var isMp4 = outputVideoFileName.EndsWith(".mp4", StringComparison.OrdinalIgnoreCase);
if (isMp4)
{
return GenerateSoftCodedVideoFileMp4(inputVideoFileName, softSubs, outputVideoFileName, outputHandler);
}
var subsInput = string.Empty;
var subsMeta = string.Empty;
var map = "-map 0";
foreach (var trackToDelete in softSubsToDelete)
{
if (!trackToDelete.IsNew && trackToDelete.Tag is MatroskaTrackInfo trackInfo)
{
map += $" -map -0:{trackInfo.TrackNumber - 1}";
}
}
var count = 1;
var number = 0;
foreach (var softSub in softSubs.Where(p => p.IsNew))
{
map += $" -map {count}";
subsInput += $" -i \"{softSub.FileName}\"";
if (!string.IsNullOrEmpty(softSub.Language))
{
var lang = string.IsNullOrEmpty(softSub.Language) ? string.Empty : softSub.Language.ToLowerInvariant();
var threeLetterCode = Iso639Dash2LanguageCode.GetThreeLetterCodeFromTwoLetterCode(lang);
if (lang.Length == 3)
{
threeLetterCode = lang;
}
else if (lang.IndexOf('-') == 2)
{
threeLetterCode = Iso639Dash2LanguageCode.GetThreeLetterCodeFromTwoLetterCode(lang.Substring(0, 2));
}
var languageName = Iso639Dash2LanguageCode.List.FirstOrDefault(p => p.ThreeLetterCode == threeLetterCode)?.EnglishName;
if (languageName == null)
{
languageName = Iso639Dash2LanguageCode.List.FirstOrDefault(p => p.TwoLetterCode == lang || p.EnglishName.ToLowerInvariant() == lang)?.EnglishName;
}
if (!string.IsNullOrEmpty(threeLetterCode) && !string.IsNullOrEmpty(languageName))
{
subsMeta += $" -metadata:s:s:{number} language=\"{threeLetterCode}\"";
subsMeta += $" -metadata:s:s:{number} title=\"{languageName}\"";
}
else if (!string.IsNullOrEmpty(softSub.Language))
{
subsMeta += $" -metadata:s:s:{number} language=\"{softSub.Language}\"";
subsMeta += $" -metadata:s:s:{number} title=\"{softSub.Language}\"";
}
}
if (softSub.IsDefault)
{
subsMeta += $" -disposition:s:s:{number} default";
}
if (softSub.IsForced)
{
subsMeta += $" -disposition:s:s:{number} forced";
subsMeta += $" -metadata:s:s:{number} forced=1";
}
count++;
number++;
}
subsInput = " " + subsInput.Trim();
if (subsInput.Trim().Length == 0)
{
subsInput = string.Empty;
}
subsMeta = " " + subsMeta.Trim();
if (subsMeta.Trim().Length == 0)
{
subsMeta = string.Empty;
}
var arguments = $"-i \"{inputVideoFileName}\" {subsInput.Trim()} {map.Trim()} -c copy {subsMeta.Trim()} \"{outputVideoFileName}\"".TrimStart();
var processMakeVideo = new Process
{
StartInfo =
{
FileName = GetFfmpegLocation(),
Arguments = arguments,
UseShellExecute = false,
CreateNoWindow = true,
}
};
processMakeVideo.StartInfo.Arguments = processMakeVideo.StartInfo.Arguments.Trim();
SetupDataReceiveHandler(outputHandler, processMakeVideo);
return processMakeVideo;
}
private static Process GenerateSoftCodedVideoFileMp4(string inputVideoFileName, List<VideoPreviewGeneratorSub> softSubs, string outputVideoFileName, DataReceivedEventHandler outputHandler)
{
var subsInput = string.Empty;
var subsMap = string.Empty;
var subsMeta = string.Empty;
var subsFormat = string.Empty;
var ffmpegInfo = FfmpegMediaInfo.Parse(inputVideoFileName);
var audioTrackCount = ffmpegInfo.Tracks.Count(p => p.TrackType == FfmpegTrackType.Audio);
var isMp4 = outputVideoFileName.EndsWith(".mp4", StringComparison.OrdinalIgnoreCase);
var count = 1;
var number = 0;
foreach (var softSub in softSubs)
@ -416,31 +514,7 @@ namespace Nikse.SubtitleEdit.Logic
subsMeta += $" -metadata:s:s:{number} forced=1";
}
if (isMp4)
{
subsFormat = " -c:s mov_text";
}
else if (softSub.SubtitleFormat == null && softSub.Format == "Blu-ray sup")
{
subsFormat += $" -c:s:s:{number} copy"; // should be "pgs" or "pgssub" or ?
}
else if (softSub.SubtitleFormat?.GetType() == typeof(SubRip))
{
subsFormat += $" -c:s:s:{number} srt";
}
else if (softSub.SubtitleFormat?.GetType() == typeof(AdvancedSubStationAlpha))
{
subsFormat += $" -c:s:s:{number} ass";
}
else if (softSub.SubtitleFormat?.GetType() == typeof(SubStationAlpha))
{
subsFormat += $" -c:s:s:{number} ssa";
}
else if (softSub.SubtitleFormat?.GetType() == typeof(WebVTT) ||
softSub.SubtitleFormat?.GetType() == typeof(WebVTTFileWithLineNumber))
{
subsFormat += $" -c:s:s:{number} webvtt";
}
subsFormat = " -c:s mov_text";
count++;
number++;

View File

@ -1,5 +1,4 @@
using Nikse.SubtitleEdit.Core.Common;
using Nikse.SubtitleEdit.Core.SubtitleFormats;
namespace Nikse.SubtitleEdit.Logic
{
@ -9,7 +8,7 @@ namespace Nikse.SubtitleEdit.Logic
public bool IsNew { get; set; }
public string FileName { get; set; }
public Subtitle Subtitle { get; set; }
public SubtitleFormat SubtitleFormat { get; set; }
public string SubtitleFormat { get; set; }
public string Format { get; set; }
public string Language { get; set; }
public bool IsForced { get; set; }