mirror of
https://github.com/bolucat/Archive.git
synced 2026-04-23 00:17:16 +08:00
226 lines
9.5 KiB
C#
226 lines
9.5 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using static BBDown.Core.Entity.Entity;
|
|
using static BBDown.BBDownUtil;
|
|
using static BBDown.Core.Util.SubUtil;
|
|
using static BBDown.Core.Logger;
|
|
using System.IO;
|
|
using BBDown.Core;
|
|
using System.Runtime.InteropServices;
|
|
|
|
namespace BBDown;
|
|
|
|
static partial class BBDownMuxer
|
|
{
|
|
public static string FFMPEG = "ffmpeg";
|
|
public static string MP4BOX = "mp4box";
|
|
|
|
private static int RunExe(string app, string parms, bool customBin = false)
|
|
{
|
|
int code = 0;
|
|
Process p = new();
|
|
p.StartInfo.FileName = app;
|
|
p.StartInfo.Arguments = parms;
|
|
p.StartInfo.UseShellExecute = false;
|
|
p.StartInfo.RedirectStandardError = true;
|
|
p.StartInfo.CreateNoWindow = false;
|
|
p.ErrorDataReceived += delegate (object sendProcess, DataReceivedEventArgs output) {
|
|
if (!string.IsNullOrWhiteSpace(output.Data))
|
|
Log(output.Data);
|
|
};
|
|
p.StartInfo.StandardErrorEncoding = Encoding.UTF8;
|
|
p.Start();
|
|
p.BeginErrorReadLine();
|
|
p.WaitForExit();
|
|
p.Close();
|
|
p.Dispose();
|
|
return code;
|
|
}
|
|
|
|
private static string EscapeString(string str)
|
|
{
|
|
return string.IsNullOrEmpty(str) ? str : str.Replace("\"", "'").Replace("\\", "\\\\");
|
|
}
|
|
|
|
private static int MuxByMp4box(string url, string videoPath, string audioPath, string outPath, string desc, string title, string author, string episodeId, string pic, string lang, List<Subtitle>? subs, bool audioOnly, bool videoOnly, List<ViewPoint>? points)
|
|
{
|
|
StringBuilder inputArg = new();
|
|
StringBuilder metaArg = new();
|
|
int nowId = 0;
|
|
inputArg.Append(" -inter 500 -noprog ");
|
|
if (!string.IsNullOrEmpty(videoPath))
|
|
{
|
|
inputArg.Append($" -add \"{videoPath}#trackID={(audioOnly && audioPath == "" ? "2" : "1")}:name=\" ");
|
|
nowId++;
|
|
}
|
|
if (!string.IsNullOrEmpty(audioPath))
|
|
{
|
|
inputArg.Append($" -add \"{audioPath}:lang={(lang == "" ? "und" : lang)}\" ");
|
|
nowId++;
|
|
}
|
|
if (points != null && points.Any())
|
|
{
|
|
var meta = GetMp4boxMetaString(points);
|
|
var metaFile = Path.Combine(Path.GetDirectoryName(string.IsNullOrEmpty(videoPath) ? audioPath : videoPath)!, "chapters");
|
|
File.WriteAllText(metaFile, meta);
|
|
inputArg.Append($" -chap \"{metaFile}\" ");
|
|
}
|
|
if (!string.IsNullOrEmpty(pic))
|
|
metaArg.Append($":cover=\"{pic}\"");
|
|
if (!string.IsNullOrEmpty(episodeId))
|
|
metaArg.Append($":album=\"{title}\":title=\"{episodeId}\"");
|
|
else
|
|
metaArg.Append($":title=\"{title}\"");
|
|
metaArg.Append($":sdesc=\"{desc}\"");
|
|
metaArg.Append($":comment=\"{url}\"");
|
|
metaArg.Append($":artist=\"{author}\"");
|
|
|
|
if (subs != null)
|
|
{
|
|
for (int i = 0; i < subs.Count; i++)
|
|
{
|
|
if (File.Exists(subs[i].path) && File.ReadAllText(subs[i].path!) != "")
|
|
{
|
|
nowId++;
|
|
inputArg.Append($" -add \"{subs[i].path}#trackID=1:name=:hdlr=sbtl:lang={GetSubtitleCode(subs[i].lan).Item1}\" ");
|
|
inputArg.Append($" -udta {nowId}:type=name:str=\"{GetSubtitleCode(subs[i].lan).Item2}\" ");
|
|
}
|
|
}
|
|
}
|
|
|
|
//----分析完毕
|
|
var arguments = (Config.DEBUG_LOG ? " -v " : "") + inputArg + (metaArg.ToString() == "" ? "" : " -itags tool=" + metaArg) + $" -new -- \"{outPath}\"";
|
|
LogDebug("mp4box命令: {0}", arguments);
|
|
return RunExe(MP4BOX, arguments, MP4BOX != "mp4box");
|
|
}
|
|
|
|
public static int MuxAV(bool useMp4box, string bvid, string videoPath, string audioPath, List<AudioMaterial> audioMaterial, string outPath, string desc = "", string title = "", string author = "", string episodeId = "", string pic = "", string lang = "", List<Subtitle>? subs = null, bool audioOnly = false, bool videoOnly = false, List<ViewPoint>? points = null, long pubTime = 0, bool simplyMux = false, bool isHevc = false)
|
|
{
|
|
if (audioOnly && audioPath != "")
|
|
videoPath = "";
|
|
if (videoOnly)
|
|
audioPath = "";
|
|
desc = EscapeString(desc);
|
|
title = EscapeString(title);
|
|
episodeId = EscapeString(episodeId);
|
|
var url = $"https://www.bilibili.com/video/{bvid}/";
|
|
|
|
if (useMp4box)
|
|
{
|
|
return MuxByMp4box(url, videoPath, audioPath, outPath, desc, title, author, episodeId, pic, lang, subs, audioOnly, videoOnly, points);
|
|
}
|
|
|
|
if (outPath.Contains('/') && ! Directory.Exists(Path.GetDirectoryName(outPath)))
|
|
Directory.CreateDirectory(Path.GetDirectoryName(outPath)!);
|
|
//----分析并生成-i参数
|
|
StringBuilder inputArg = new();
|
|
StringBuilder metaArg = new();
|
|
byte inputCount = 0;
|
|
foreach (string path in new[] { videoPath, audioPath })
|
|
{
|
|
if (!string.IsNullOrEmpty(path))
|
|
{
|
|
inputCount++;
|
|
inputArg.Append($"-i \"{path}\" ");
|
|
}
|
|
}
|
|
|
|
if (audioMaterial.Any())
|
|
{
|
|
byte audioCount = 0;
|
|
metaArg.Append("-metadata:s:a:0 title=\"原音频\" ");
|
|
foreach (var audio in audioMaterial)
|
|
{
|
|
inputCount++;
|
|
audioCount++;
|
|
inputArg.Append($"-i \"{audio.path}\" ");
|
|
if (!string.IsNullOrWhiteSpace(audio.title)) metaArg.Append($"-metadata:s:a:{audioCount} title=\"{audio.title}\" ");
|
|
if (!string.IsNullOrWhiteSpace(audio.personName)) metaArg.Append($"-metadata:s:a:{audioCount} artist=\"{audio.personName}\" ");
|
|
}
|
|
}
|
|
|
|
if (!string.IsNullOrEmpty(pic))
|
|
{
|
|
inputCount++;
|
|
inputArg.Append($"-i \"{pic}\" ");
|
|
}
|
|
|
|
if (subs != null)
|
|
{
|
|
for (int i = 0; i < subs.Count; i++)
|
|
{
|
|
if(File.Exists(subs[i].path) && File.ReadAllText(subs[i].path!) != "")
|
|
{
|
|
inputCount++;
|
|
inputArg.Append($"-i \"{subs[i].path}\" ");
|
|
metaArg.Append($"-metadata:s:s:{i} title=\"{GetSubtitleCode(subs[i].lan).Item2}\" -metadata:s:s:{i} language={GetSubtitleCode(subs[i].lan).Item1} ");
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!string.IsNullOrEmpty(pic))
|
|
metaArg.Append($"-disposition:v:{(audioOnly ? "0" : "1")} attached_pic ");
|
|
// var inputCount = InputRegex().Matches(inputArg.ToString()).Count;
|
|
|
|
if (points != null && points.Any())
|
|
{
|
|
var meta = GetFFmpegMetaString(points);
|
|
var metaFile = Path.Combine(Path.GetDirectoryName(string.IsNullOrEmpty(videoPath) ? audioPath : videoPath)!, "chapters");
|
|
File.WriteAllText(metaFile, meta);
|
|
inputArg.Append($"-i \"{metaFile}\" -map_chapters {inputCount} ");
|
|
}
|
|
|
|
inputArg.Append(string.Concat(Enumerable.Range(0, inputCount).Select(i => $"-map {i} ")));
|
|
|
|
//----分析完毕
|
|
StringBuilder argsBuilder = new StringBuilder();
|
|
argsBuilder.Append($"-loglevel {(Config.DEBUG_LOG ? "verbose" : "warning")} -y ");
|
|
argsBuilder.Append(inputArg);
|
|
argsBuilder.Append(metaArg);
|
|
if (!simplyMux) {
|
|
argsBuilder.Append($"-metadata title=\"{(episodeId == "" ? title : episodeId)}\" ");
|
|
argsBuilder.Append($"-metadata comment=\"{url}\" ");
|
|
if (lang != "") argsBuilder.Append($"-metadata:s:a:0 language={lang} ");
|
|
if (!string.IsNullOrWhiteSpace(desc)) argsBuilder.Append($"-metadata description=\"{desc}\" ");
|
|
if (!string.IsNullOrEmpty(author)) argsBuilder.Append($"-metadata artist=\"{author}\" ");
|
|
if (episodeId != "") argsBuilder.Append($"-metadata album=\"{title}\" ");
|
|
if (pubTime != 0) argsBuilder.Append($"-metadata creation_time=\"{(DateTimeOffset.FromUnixTimeSeconds(pubTime).ToString("yyyy-MM-ddTHH:mm:ss.ffffffZ"))}\" ");
|
|
}
|
|
argsBuilder.Append("-c:v copy -c:a copy ");
|
|
if (audioOnly && audioPath == "") argsBuilder.Append("-vn ");
|
|
if (subs != null) argsBuilder.Append("-c:s mov_text ");
|
|
// fix macOS hev1, see https://discussions.apple.com/thread/253081863?sortBy=rank
|
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX) && isHevc) argsBuilder.Append("-tag:v:0 hvc1 ");
|
|
argsBuilder.Append($"-movflags faststart -strict unofficial -strict -2 -f mp4 -- \"{outPath}\"");
|
|
|
|
string arguments = argsBuilder.ToString();
|
|
|
|
LogDebug("ffmpeg命令: {0}", arguments);
|
|
return RunExe(FFMPEG, arguments, FFMPEG != "ffmpeg");
|
|
}
|
|
|
|
public static void MergeFLV(string[] files, string outPath)
|
|
{
|
|
if (files.Length == 1)
|
|
{
|
|
File.Move(files[0], outPath);
|
|
}
|
|
else
|
|
{
|
|
foreach (var file in files)
|
|
{
|
|
var tmpFile = Path.Combine(Path.GetDirectoryName(file)!, Path.GetFileNameWithoutExtension(file) + ".ts");
|
|
var arguments = $"-loglevel warning -y -i \"{file}\" -map 0 -c copy -f mpegts -bsf:v h264_mp4toannexb \"{tmpFile}\"";
|
|
LogDebug("ffmpeg命令: {0}", arguments);
|
|
RunExe("ffmpeg", arguments);
|
|
File.Delete(file);
|
|
}
|
|
var f = GetFiles(Path.GetDirectoryName(files[0])!, ".ts");
|
|
CombineMultipleFilesIntoSingleFile(f, outPath);
|
|
foreach (var s in f) File.Delete(s);
|
|
}
|
|
}
|
|
} |