优化直播字幕录制逻辑

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)
{
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,
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();
List<Mediainfo> mediaInfos = new();
FileStream? fileOutputStream = null;
WebVttSub currentVtt = new(); //字幕流始终维护一个实例
bool firstSub = true;
task.MaxValue = 0;
task.StartTask();
@ -283,42 +285,27 @@ namespace N_m3u8DL_RE.DownloadManager
if (DownloaderConfig.MyOptions.AutoSubtitleFix && streamSpec.MediaType == Common.Enum.MediaType.SUBTITLES
&& 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);
foreach (var seg in keys)
{
var vttContent = File.ReadAllText(FileDic[seg]!.ActualFilePath);
var vtt = WebVttSub.Parse(vttContent);
//手动计算MPEGTS
if (finalVtt.MpegtsTimestamp == 0 && vtt.MpegtsTimestamp == 0)
if (currentVtt.MpegtsTimestamp == 0 && vtt.MpegtsTimestamp == 0)
{
vtt.MpegtsTimestamp = 90 * (long)(seg.Duration * 1000) * seg.Index;
}
if (first)
if (firstSub)
{
finalVtt = vtt;
first = false;
currentVtt = vtt;
firstSub = false;
}
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字幕
@ -330,21 +317,16 @@ namespace N_m3u8DL_RE.DownloadManager
var (sawVtt, timescale) = MP4VttUtil.CheckInit(iniFileBytes);
if (sawVtt)
{
Logger.WarnMarkUp(ResString.fixingVTTmp4);
var mp4s = FileDic.Values.Select(v => v!.ActualFilePath).Where(p => p.EndsWith(".m4s")).OrderBy(s => s).ToArray();
var finalVtt = MP4VttUtil.ExtractSub(mp4s, timescale);
//写出字幕
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()
if (firstSub)
{
ActualContentLength = subContentFixed.Length,
ActualFilePath = path
};
currentVtt = MP4VttUtil.ExtractSub(mp4s, timescale);
}
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
&& 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 finalVtt = MP4TtmlUtil.ExtractFromTTMLs(mp4s, 0);
//写出字幕
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()
if (firstSub)
{
ActualContentLength = subContentFixed.Length,
ActualFilePath = path
};
currentVtt = MP4TtmlUtil.ExtractFromTTMLs(mp4s, 0);
}
else
{
var finalVtt = MP4TtmlUtil.ExtractFromTTMLs(mp4s, 0);
currentVtt.AddCuesFromOne(finalVtt);
}
}
//自动修复TTML mp4字幕
@ -374,25 +351,20 @@ namespace N_m3u8DL_RE.DownloadManager
&& streamSpec.Extension != null && streamSpec.Extension.Contains("m4s")
&& streamSpec.Codecs != null && streamSpec.Codecs.Contains("stpp"))
{
Logger.WarnMarkUp(ResString.fixingTTMLmp4);
//sawTtml暂时不判断
//var initFile = FileDic.Values.Where(v => Path.GetFileName(v!.ActualFilePath).StartsWith("_init")).FirstOrDefault();
//var iniFileBytes = File.ReadAllBytes(initFile!.ActualFilePath);
//var sawTtml = MP4TtmlUtil.CheckInit(iniFileBytes);
var mp4s = FileDic.Values.Select(v => v!.ActualFilePath).Where(p => p.EndsWith(".m4s")).OrderBy(s => s).ToArray();
var finalVtt = MP4TtmlUtil.ExtractFromMp4s(mp4s, 0);
//写出字幕
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()
if (firstSub)
{
ActualContentLength = subContentFixed.Length,
ActualFilePath = path
};
currentVtt = MP4TtmlUtil.ExtractFromMp4s(mp4s, 0);
}
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";
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) 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);
@ -457,18 +433,28 @@ namespace N_m3u8DL_RE.DownloadManager
}
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();
foreach (var inputFilePath in files)
{
using (var inputStream = File.OpenRead(inputFilePath))
{
inputStream.CopyTo(fileOutputStream);
}
if (!DownloaderConfig.MyOptions.LiveKeepSegments && !Path.GetFileName(inputFilePath).StartsWith("_init"))
{
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