mirror of
https://github.com/yaobiao131/downkyicore.git
synced 2025-08-10 00:52:31 +00:00
239 lines
7.8 KiB
C#
239 lines
7.8 KiB
C#
using DownKyi.Core.Logging;
|
||
using DownKyi.Core.Settings;
|
||
using FFMpegCore;
|
||
using FFMpegCore.Enums;
|
||
using FFMpegCore.Helpers;
|
||
using FFMpegCore.Pipes;
|
||
|
||
namespace DownKyi.Core.FFMpeg;
|
||
|
||
public class FFMpeg
|
||
{
|
||
private const string Tag = "FFmpegHelper";
|
||
private static readonly FFMpeg instance = new();
|
||
|
||
static FFMpeg()
|
||
{
|
||
}
|
||
|
||
private FFMpeg()
|
||
{
|
||
GlobalFFOptions.Configure(new FFOptions { BinaryFolder = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "ffmpeg") });
|
||
FFMpegHelper.VerifyFFMpegExists(GlobalFFOptions.Current);
|
||
}
|
||
|
||
public static FFMpeg Instance => instance;
|
||
|
||
/// <summary>
|
||
/// 合并音频和视频
|
||
/// </summary>
|
||
/// <param name="audio">音频</param>
|
||
/// <param name="video">视频</param>
|
||
/// <param name="destVideo"></param>
|
||
public bool MergeVideo(string? audio, string? video, string destVideo)
|
||
{
|
||
if (!File.Exists(audio) && !File.Exists(video)) return false;
|
||
|
||
var arguments = FFMpegArguments
|
||
.FromFileInput(audio)
|
||
.AddFileInput(video)
|
||
.OutputToFile(destVideo, true, options => options
|
||
.WithCustomArgument("-strict -2")
|
||
.WithAudioCodec("copy")
|
||
.WithVideoCodec("copy")
|
||
.ForceFormat("mp4")
|
||
);
|
||
if (audio == null || !File.Exists(audio))
|
||
{
|
||
arguments = FFMpegArguments.FromFileInput(video).OutputToFile(
|
||
destVideo,
|
||
true,
|
||
options => options.WithCustomArgument("-strict -2").WithVideoCodec("copy").WithAudioCodec("copy").ForceFormat("mp4")
|
||
);
|
||
}
|
||
if (video == null || !File.Exists(video))
|
||
{
|
||
if (SettingsManager.GetInstance().GetIsTranscodingAacToMp3() == AllowStatus.Yes)
|
||
{
|
||
arguments = FFMpegArguments.FromFileInput(audio).OutputToFile(
|
||
destVideo,
|
||
true,
|
||
options => options.WithCustomArgument("-strict -2").DisableChannel(Channel.Video).ForceFormat("mp3")
|
||
);
|
||
}
|
||
else
|
||
{
|
||
arguments = FFMpegArguments.FromFileInput(audio).OutputToFile(
|
||
destVideo,
|
||
true,
|
||
options => options.WithCustomArgument("-strict -2").DisableChannel(Channel.Video).WithAudioCodec("copy")
|
||
);
|
||
}
|
||
}
|
||
|
||
LogManager.Debug(Tag, arguments.Arguments);
|
||
|
||
arguments
|
||
.NotifyOnError(s => LogManager.Debug(Tag, s))
|
||
.ProcessSynchronously(false);
|
||
try
|
||
{
|
||
if (audio != null)
|
||
{
|
||
File.Delete(audio);
|
||
}
|
||
|
||
if (video != null)
|
||
{
|
||
File.Delete(video);
|
||
}
|
||
}
|
||
catch (IOException e)
|
||
{
|
||
Console.WriteLine("MergeVideo()发生IO异常: {0}", e);
|
||
LogManager.Error(Tag, e);
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 去水印,非常消耗cpu资源
|
||
/// </summary>
|
||
/// <param name="video"></param>
|
||
/// <param name="destVideo"></param>
|
||
/// <param name="x"></param>
|
||
/// <param name="y"></param>
|
||
/// <param name="width"></param>
|
||
/// <param name="height"></param>
|
||
/// <param name="action"></param>
|
||
public void Delogo(string video, string destVideo, int x, int y, int width, int height, Action<string> action)
|
||
{
|
||
FFMpegArguments
|
||
.FromFileInput(video)
|
||
.OutputToFile(
|
||
destVideo,
|
||
true,
|
||
option => option
|
||
.WithCustomArgument($"-vf delogo=x={x}:y={y}:w={width}:h={height}:show=0 -hide_banner"))
|
||
.NotifyOnOutput(action.Invoke)
|
||
.NotifyOnError(action.Invoke)
|
||
.ProcessSynchronously(false);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 从一个视频中仅提取音频
|
||
/// </summary>
|
||
/// <param name="video">源视频</param>
|
||
/// <param name="audio">目标音频</param>
|
||
/// <param name="action">输出信息</param>
|
||
public void ExtractAudio(string video, string audio, Action<string> action)
|
||
{
|
||
FFMpegArguments
|
||
.FromFileInput(video)
|
||
.OutputToFile(audio,
|
||
true,
|
||
options => options
|
||
.WithCustomArgument("-hide_banner")
|
||
.WithAudioCodec("copy")
|
||
.DisableChannel(Channel.Video)
|
||
)
|
||
.NotifyOnOutput(action.Invoke)
|
||
.NotifyOnError(action.Invoke)
|
||
.ProcessSynchronously(false);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 从一个视频中仅提取视频
|
||
/// </summary>
|
||
/// <param name="video">源视频</param>
|
||
/// <param name="destVideo">目标视频</param>
|
||
/// <param name="action">输出信息</param>
|
||
public void ExtractVideo(string video, string destVideo, Action<string> action)
|
||
{
|
||
FFMpegArguments.FromFileInput(video)
|
||
.OutputToFile(
|
||
destVideo,
|
||
true,
|
||
options => options
|
||
.WithCustomArgument("-hide_banner")
|
||
.WithVideoCodec("copy")
|
||
.DisableChannel(Channel.Audio))
|
||
.NotifyOnOutput(action.Invoke)
|
||
.NotifyOnError(action.Invoke)
|
||
.ProcessSynchronously(false);
|
||
}
|
||
|
||
|
||
public async Task<MemoryStream> ExtractVideoFrame(string inputPath,TimeSpan timestamp)
|
||
{
|
||
var ms = new MemoryStream();
|
||
await FFMpegArguments
|
||
.FromFileInput(inputPath, false, options => options.Seek(timestamp))
|
||
.OutputToPipe(new StreamPipeSink(ms), options => options
|
||
.WithFrameOutputCount(1)
|
||
.ForceFormat("image2")
|
||
.WithVideoCodec("mjpeg")
|
||
)
|
||
.NotifyOnError(x => Console.WriteLine(x))
|
||
.ProcessAsynchronously(false);
|
||
ms.Position = 0;
|
||
return ms;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 合并多个FLV视频片段为一个完整视频
|
||
/// </summary>
|
||
/// <param name="inputFlvs">FLV片段路径列表(按顺序)</param>
|
||
/// <param name="outputVideo">输出视频路径</param>
|
||
/// <param name="action">进度回调</param>
|
||
/// <returns>是否成功</returns>
|
||
public bool ConcatVideos(List<string> inputFlvs, string outputVideo, Action<string> action)
|
||
{
|
||
try
|
||
{
|
||
if (inputFlvs == null || inputFlvs.Count == 0)
|
||
{
|
||
return false;
|
||
}
|
||
|
||
// 验证所有输入文件都存在
|
||
foreach (var video in inputFlvs)
|
||
{
|
||
if (!File.Exists(video))
|
||
{
|
||
action?.Invoke($"文件不存在: {video}");
|
||
return false;
|
||
}
|
||
}
|
||
|
||
LogManager.Debug(Tag, $"开始合并 {inputFlvs.Count} 个视频到 {outputVideo}");
|
||
|
||
|
||
var listFile = Path.Combine(Path.GetTempPath(), $"flvlist_{DateTime.Now:yyyyMMddHHmmss}.txt");
|
||
File.WriteAllLines(listFile, inputFlvs.Select(f => $"file '{f.Replace("'", "'\\''")}'"));
|
||
|
||
FFMpegArguments
|
||
.FromFileInput(listFile, false, options => options
|
||
.WithCustomArgument("-f concat -safe 0"))
|
||
.OutputToFile(outputVideo, true, options => options
|
||
.WithVideoCodec("libx264")
|
||
.WithAudioCodec("aac")
|
||
.WithCustomArgument("-movflags +faststart")
|
||
.WithCustomArgument("-avoid_negative_ts make_zero")
|
||
)
|
||
.NotifyOnOutput(action.Invoke)
|
||
.NotifyOnError(action.Invoke)
|
||
.ProcessSynchronously(false);
|
||
|
||
try { File.Delete(listFile); } catch { }
|
||
LogManager.Debug(Tag, "视频合并完成");
|
||
return true;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error(Tag, ex);
|
||
return false;
|
||
}
|
||
}
|
||
} |