优化直播字幕录制逻辑

This commit is contained in:
nilaoda 2022-09-19 01:29:44 +08:00
parent 80b6f85578
commit 0f89ecc7cc
2 changed files with 51 additions and 65 deletions

View File

@ -334,7 +334,7 @@ namespace N_m3u8DL_RE.CommandLine
public static async Task<int> InvokeArgs(string[] args, Func<MyOption, Task> action) public static async Task<int> InvokeArgs(string[] args, Func<MyOption, Task> action)
{ {
var rootCommand = new RootCommand("N_m3u8DL-RE (Beta version) 20220918") var rootCommand = new RootCommand("N_m3u8DL-RE (Beta version) 20220919")
{ {
Input, TmpDir, SaveDir, SaveName, BaseUrl, ThreadCount, DownloadRetryCount, AutoSelect, SkipMerge, SkipDownload, CheckSegmentsCount, Input, TmpDir, SaveDir, SaveName, BaseUrl, ThreadCount, DownloadRetryCount, AutoSelect, SkipMerge, SkipDownload, CheckSegmentsCount,
BinaryMerge, DelAfterDone, WriteMetaJson, AppendUrlParams, ConcurrentDownload, Headers, /**SavePattern,**/ SubOnly, SubtitleFormat, AutoSubtitleFix, BinaryMerge, DelAfterDone, WriteMetaJson, AppendUrlParams, ConcurrentDownload, Headers, /**SavePattern,**/ SubOnly, SubtitleFormat, AutoSubtitleFix,

View File

@ -126,6 +126,8 @@ namespace N_m3u8DL_RE.DownloadManager
ConcurrentDictionary<MediaSegment, DownloadResult?> FileDic = new(); ConcurrentDictionary<MediaSegment, DownloadResult?> FileDic = new();
List<Mediainfo> mediaInfos = new(); List<Mediainfo> mediaInfos = new();
FileStream? fileOutputStream = null; FileStream? fileOutputStream = null;
WebVttSub currentVtt = new(); //字幕流始终维护一个实例
bool firstSub = true;
task.MaxValue = 0; task.MaxValue = 0;
task.StartTask(); task.StartTask();
@ -283,42 +285,27 @@ namespace N_m3u8DL_RE.DownloadManager
if (DownloaderConfig.MyOptions.AutoSubtitleFix && streamSpec.MediaType == Common.Enum.MediaType.SUBTITLES if (DownloaderConfig.MyOptions.AutoSubtitleFix && streamSpec.MediaType == Common.Enum.MediaType.SUBTITLES
&& streamSpec.Extension != null && streamSpec.Extension.Contains("vtt")) && streamSpec.Extension != null && streamSpec.Extension.Contains("vtt"))
{ {
Logger.WarnMarkUp(ResString.fixingVTT);
//排序字幕并修正时间戳 //排序字幕并修正时间戳
bool first = true;
var finalVtt = new WebVttSub();
var keys = FileDic.Keys.OrderBy(k => k.Index); var keys = FileDic.Keys.OrderBy(k => k.Index);
foreach (var seg in keys) foreach (var seg in keys)
{ {
var vttContent = File.ReadAllText(FileDic[seg]!.ActualFilePath); var vttContent = File.ReadAllText(FileDic[seg]!.ActualFilePath);
var vtt = WebVttSub.Parse(vttContent); var vtt = WebVttSub.Parse(vttContent);
//手动计算MPEGTS //手动计算MPEGTS
if (finalVtt.MpegtsTimestamp == 0 && vtt.MpegtsTimestamp == 0) if (currentVtt.MpegtsTimestamp == 0 && vtt.MpegtsTimestamp == 0)
{ {
vtt.MpegtsTimestamp = 90 * (long)(seg.Duration * 1000) * seg.Index; vtt.MpegtsTimestamp = 90 * (long)(seg.Duration * 1000) * seg.Index;
} }
if (first) if (firstSub)
{ {
finalVtt = vtt; currentVtt = vtt;
first = false; firstSub = false;
} }
else else
{ {
finalVtt.AddCuesFromOne(vtt); currentVtt.AddCuesFromOne(vtt);
} }
} }
//写出字幕
var files = FileDic.Values.Where(v => !Path.GetFileName(v!.ActualFilePath).StartsWith("_init")).Select(v => v!.ActualFilePath).OrderBy(s => s).ToArray();
foreach (var item in files) File.Delete(item);
FileDic.Clear();
var path = Path.Combine(tmpDir, Path.GetFileNameWithoutExtension(files.Last()) + ".vtt");
var subContentFixed = finalVtt.ToString();
await File.WriteAllTextAsync(path, subContentFixed, Encoding.UTF8);
FileDic[keys.First()] = new DownloadResult()
{
ActualContentLength = subContentFixed.Length,
ActualFilePath = path
};
} }
//自动修复VTT mp4字幕 //自动修复VTT mp4字幕
@ -330,21 +317,16 @@ namespace N_m3u8DL_RE.DownloadManager
var (sawVtt, timescale) = MP4VttUtil.CheckInit(iniFileBytes); var (sawVtt, timescale) = MP4VttUtil.CheckInit(iniFileBytes);
if (sawVtt) if (sawVtt)
{ {
Logger.WarnMarkUp(ResString.fixingVTTmp4);
var mp4s = FileDic.Values.Select(v => v!.ActualFilePath).Where(p => p.EndsWith(".m4s")).OrderBy(s => s).ToArray(); var mp4s = FileDic.Values.Select(v => v!.ActualFilePath).Where(p => p.EndsWith(".m4s")).OrderBy(s => s).ToArray();
var finalVtt = MP4VttUtil.ExtractSub(mp4s, timescale); if (firstSub)
//写出字幕
var firstKey = FileDic.Keys.First();
foreach (var item in mp4s) File.Delete(item);
FileDic.Clear();
var path = Path.Combine(tmpDir, Path.GetFileNameWithoutExtension(mp4s.Last()) + ".vtt");
var subContentFixed = finalVtt.ToString();
await File.WriteAllTextAsync(path, subContentFixed, Encoding.UTF8);
FileDic[firstKey] = new DownloadResult()
{ {
ActualContentLength = subContentFixed.Length, currentVtt = MP4VttUtil.ExtractSub(mp4s, timescale);
ActualFilePath = path }
}; else
{
var finalVtt = MP4VttUtil.ExtractSub(mp4s, timescale);
currentVtt.AddCuesFromOne(finalVtt);
}
} }
} }
@ -352,21 +334,16 @@ namespace N_m3u8DL_RE.DownloadManager
if (DownloaderConfig.MyOptions.AutoSubtitleFix && streamSpec.MediaType == Common.Enum.MediaType.SUBTITLES if (DownloaderConfig.MyOptions.AutoSubtitleFix && streamSpec.MediaType == Common.Enum.MediaType.SUBTITLES
&& streamSpec.Extension != null && streamSpec.Extension.Contains("ttml")) && streamSpec.Extension != null && streamSpec.Extension.Contains("ttml"))
{ {
Logger.WarnMarkUp(ResString.fixingTTML);
var mp4s = FileDic.Values.Select(v => v!.ActualFilePath).Where(p => p.EndsWith(".ttml")).OrderBy(s => s).ToArray(); var mp4s = FileDic.Values.Select(v => v!.ActualFilePath).Where(p => p.EndsWith(".ttml")).OrderBy(s => s).ToArray();
var finalVtt = MP4TtmlUtil.ExtractFromTTMLs(mp4s, 0); if (firstSub)
//写出字幕
var firstKey = FileDic.Keys.First();
foreach (var item in mp4s) File.Delete(item);
FileDic.Clear();
var path = Path.Combine(tmpDir, Path.GetFileNameWithoutExtension(mp4s.Last()) + ".vtt");
var subContentFixed = finalVtt.ToString();
await File.WriteAllTextAsync(path, subContentFixed, Encoding.UTF8);
FileDic[firstKey] = new DownloadResult()
{ {
ActualContentLength = subContentFixed.Length, currentVtt = MP4TtmlUtil.ExtractFromTTMLs(mp4s, 0);
ActualFilePath = path }
}; else
{
var finalVtt = MP4TtmlUtil.ExtractFromTTMLs(mp4s, 0);
currentVtt.AddCuesFromOne(finalVtt);
}
} }
//自动修复TTML mp4字幕 //自动修复TTML mp4字幕
@ -374,25 +351,20 @@ namespace N_m3u8DL_RE.DownloadManager
&& streamSpec.Extension != null && streamSpec.Extension.Contains("m4s") && streamSpec.Extension != null && streamSpec.Extension.Contains("m4s")
&& streamSpec.Codecs != null && streamSpec.Codecs.Contains("stpp")) && streamSpec.Codecs != null && streamSpec.Codecs.Contains("stpp"))
{ {
Logger.WarnMarkUp(ResString.fixingTTMLmp4);
//sawTtml暂时不判断 //sawTtml暂时不判断
//var initFile = FileDic.Values.Where(v => Path.GetFileName(v!.ActualFilePath).StartsWith("_init")).FirstOrDefault(); //var initFile = FileDic.Values.Where(v => Path.GetFileName(v!.ActualFilePath).StartsWith("_init")).FirstOrDefault();
//var iniFileBytes = File.ReadAllBytes(initFile!.ActualFilePath); //var iniFileBytes = File.ReadAllBytes(initFile!.ActualFilePath);
//var sawTtml = MP4TtmlUtil.CheckInit(iniFileBytes); //var sawTtml = MP4TtmlUtil.CheckInit(iniFileBytes);
var mp4s = FileDic.Values.Select(v => v!.ActualFilePath).Where(p => p.EndsWith(".m4s")).OrderBy(s => s).ToArray(); var mp4s = FileDic.Values.Select(v => v!.ActualFilePath).Where(p => p.EndsWith(".m4s")).OrderBy(s => s).ToArray();
var finalVtt = MP4TtmlUtil.ExtractFromMp4s(mp4s, 0); if (firstSub)
//写出字幕
var firstKey = FileDic.Keys.First();
foreach (var item in mp4s) File.Delete(item);
FileDic.Clear();
var path = Path.Combine(tmpDir, Path.GetFileNameWithoutExtension(mp4s.Last()) + ".vtt");
var subContentFixed = finalVtt.ToString();
await File.WriteAllTextAsync(path, subContentFixed, Encoding.UTF8);
FileDic[firstKey] = new DownloadResult()
{ {
ActualContentLength = subContentFixed.Length, currentVtt = MP4TtmlUtil.ExtractFromMp4s(mp4s, 0);
ActualFilePath = path }
}; else
{
var finalVtt = MP4TtmlUtil.ExtractFromMp4s(mp4s, 0);
currentVtt.AddCuesFromOne(finalVtt);
}
} }
//合并逻辑 //合并逻辑
@ -403,7 +375,11 @@ namespace N_m3u8DL_RE.DownloadManager
if (streamSpec.Extension == null) outputExt = ".ts"; if (streamSpec.Extension == null) outputExt = ".ts";
else if (streamSpec.MediaType == MediaType.AUDIO && streamSpec.Extension == "m4s") outputExt = ".m4a"; else if (streamSpec.MediaType == MediaType.AUDIO && streamSpec.Extension == "m4s") outputExt = ".m4a";
else if (streamSpec.MediaType != MediaType.SUBTITLES && streamSpec.Extension == "m4s") outputExt = ".mp4"; else if (streamSpec.MediaType != MediaType.SUBTITLES && streamSpec.Extension == "m4s") outputExt = ".mp4";
else if (streamSpec.MediaType == MediaType.SUBTITLES) outputExt = ".vtt"; else if (streamSpec.MediaType == MediaType.SUBTITLES)
{
if (DownloaderConfig.MyOptions.SubtitleFormat == Enum.SubtitleFormat.SRT) outputExt = ".srt";
else outputExt = ".vtt";
}
var output = Path.Combine(saveDir, saveName + outputExt); var output = Path.Combine(saveDir, saveName + outputExt);
@ -457,18 +433,28 @@ namespace N_m3u8DL_RE.DownloadManager
} }
else else
{ {
var initResult = streamSpec.Playlist!.MediaInit != null ? FileDic[streamSpec.Playlist!.MediaInit!]! : null;
var files = FileDic.Select(f => f.Value).Select(v => v!.ActualFilePath).OrderBy(s => s).ToArray(); var files = FileDic.Select(f => f.Value).Select(v => v!.ActualFilePath).OrderBy(s => s).ToArray();
foreach (var inputFilePath in files) foreach (var inputFilePath in files)
{ {
using (var inputStream = File.OpenRead(inputFilePath))
{
inputStream.CopyTo(fileOutputStream);
}
if (!DownloaderConfig.MyOptions.LiveKeepSegments && !Path.GetFileName(inputFilePath).StartsWith("_init")) if (!DownloaderConfig.MyOptions.LiveKeepSegments && !Path.GetFileName(inputFilePath).StartsWith("_init"))
{ {
File.Delete(inputFilePath); File.Delete(inputFilePath);
} }
} }
var subText = currentVtt.ToStringWithHeader();
if (outputExt == ".srt")
{
subText = OtherUtil.WebVtt2Other(currentVtt, Enum.SubtitleFormat.SRT);
}
var subBytes = Encoding.UTF8.GetBytes(subText);
fileOutputStream.Position = 0;
fileOutputStream.Write(subBytes);
FileDic.Clear();
if (initResult != null)
{
FileDic[streamSpec.Playlist!.MediaInit!] = initResult;
}
} }
//刷新buffer //刷新buffer