feat: add fnOS packaging, storage workflows and release pipeline
This commit is contained in:
+65
-49
@@ -236,12 +236,11 @@ namespace dy.net.utils
|
||||
if (imageFilePaths == null || !imageFilePaths.Any())
|
||||
throw new ArgumentException("图片路径列表不能为空。", nameof(imageFilePaths));
|
||||
|
||||
if (string.IsNullOrEmpty(audioFilePath) || !File.Exists(audioFilePath))
|
||||
throw new FileNotFoundException("音频文件未找到。", audioFilePath);
|
||||
|
||||
if (string.IsNullOrEmpty(outputVideoPath))
|
||||
throw new ArgumentNullException(nameof(outputVideoPath));
|
||||
|
||||
var hasAudio = !string.IsNullOrWhiteSpace(audioFilePath) && File.Exists(audioFilePath);
|
||||
|
||||
foreach (var imagePath in imageFilePaths)
|
||||
{
|
||||
if (!File.Exists(imagePath.Path))
|
||||
@@ -278,7 +277,7 @@ namespace dy.net.utils
|
||||
|
||||
// 获取音频时长
|
||||
double audioDurationSeconds = imageList.Count * ImageDisplayDurationSeconds;
|
||||
try
|
||||
if (hasAudio) try
|
||||
{
|
||||
var startInfo = new ProcessStartInfo
|
||||
{
|
||||
@@ -329,50 +328,10 @@ namespace dy.net.utils
|
||||
// 组合最终滤镜:先适配尺寸,再循环
|
||||
string filterComplex = $"[0:v]{fitFilter},loop=loop={loopCount - 1}:size={imageCount}[v]";
|
||||
|
||||
var arguments = new List<string>
|
||||
{
|
||||
"-y", // 覆盖输出文件
|
||||
|
||||
// 图片序列输入
|
||||
"-f", "image2",
|
||||
"-framerate", imageFps.ToString(CultureInfo.InvariantCulture),
|
||||
"-start_number", "1", // 修正:临时文件是 temp_001、temp_002... 所以起始编号为1
|
||||
"-i", imageSequencePattern,
|
||||
|
||||
// 音频输入
|
||||
"-i", audioFilePath,
|
||||
|
||||
// 滤镜:尺寸适配 + 循环
|
||||
"-filter_complex", filterComplex,
|
||||
|
||||
// 流映射
|
||||
"-map", "[v]",
|
||||
"-map", "1:a",
|
||||
|
||||
// 视频编码参数
|
||||
"-c:v", VideoCodec,
|
||||
"-preset", VideoPreset,
|
||||
"-crf", $"{VideoCrf}",
|
||||
"-s", $"{VideoWidth}x{VideoHeight}",
|
||||
"-pix_fmt", "yuv420p",
|
||||
"-profile:v", "main",
|
||||
|
||||
// 音频编码参数
|
||||
"-c:a", AudioCodec,
|
||||
"-b:a", $"{AudioBitrate}",
|
||||
"-ac", "2",
|
||||
"-ar", "44100",
|
||||
|
||||
// 封装优化
|
||||
"-f", "mp4",
|
||||
"-movflags", "+faststart",
|
||||
|
||||
// 同步参数
|
||||
"-shortest",
|
||||
|
||||
// 输出路径
|
||||
outputVideoPath
|
||||
};
|
||||
var arguments = BuildImageVideoArguments(
|
||||
imageSequencePattern, hasAudio ? audioFilePath : null, outputVideoPath,
|
||||
imageFps, filterComplex, VideoWidth, VideoHeight,
|
||||
VideoCodec, VideoPreset, VideoCrf, AudioCodec, AudioBitrate);
|
||||
|
||||
// 执行FFmpeg命令
|
||||
await ExecuteFFmpegAsync(arguments, progress, cancellationToken);
|
||||
@@ -401,6 +360,63 @@ namespace dy.net.utils
|
||||
}
|
||||
}
|
||||
|
||||
internal static List<string> BuildImageVideoArguments(
|
||||
string imageSequencePattern,
|
||||
string audioFilePath,
|
||||
string outputVideoPath,
|
||||
double imageFps,
|
||||
string filterComplex,
|
||||
int videoWidth,
|
||||
int videoHeight,
|
||||
string videoCodec = "libx264",
|
||||
string videoPreset = "medium",
|
||||
int videoCrf = 23,
|
||||
string audioCodec = "aac",
|
||||
string audioBitrate = "192k")
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(imageSequencePattern)) throw new ArgumentNullException(nameof(imageSequencePattern));
|
||||
if (string.IsNullOrWhiteSpace(outputVideoPath)) throw new ArgumentNullException(nameof(outputVideoPath));
|
||||
if (string.IsNullOrWhiteSpace(filterComplex)) throw new ArgumentNullException(nameof(filterComplex));
|
||||
|
||||
var hasAudio = !string.IsNullOrWhiteSpace(audioFilePath) && File.Exists(audioFilePath);
|
||||
var arguments = new List<string>
|
||||
{
|
||||
"-y",
|
||||
"-f", "image2",
|
||||
"-framerate", imageFps.ToString(CultureInfo.InvariantCulture),
|
||||
"-start_number", "1",
|
||||
"-i", imageSequencePattern
|
||||
};
|
||||
if (hasAudio) arguments.AddRange(new[] { "-i", audioFilePath });
|
||||
arguments.AddRange(new[]
|
||||
{
|
||||
"-filter_complex", filterComplex,
|
||||
"-map", "[v]",
|
||||
"-c:v", videoCodec,
|
||||
"-preset", videoPreset,
|
||||
"-crf", $"{videoCrf}",
|
||||
"-s", $"{videoWidth}x{videoHeight}",
|
||||
"-pix_fmt", "yuv420p",
|
||||
"-profile:v", "main"
|
||||
});
|
||||
if (hasAudio)
|
||||
{
|
||||
arguments.AddRange(new[]
|
||||
{
|
||||
"-map", "1:a",
|
||||
"-c:a", audioCodec,
|
||||
"-b:a", audioBitrate,
|
||||
"-ac", "2",
|
||||
"-ar", "44100"
|
||||
});
|
||||
}
|
||||
else arguments.Add("-an");
|
||||
arguments.AddRange(new[] { "-f", "mp4", "-movflags", "+faststart" });
|
||||
if (hasAudio) arguments.Add("-shortest");
|
||||
arguments.Add(outputVideoPath);
|
||||
return arguments;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -782,4 +798,4 @@ namespace dy.net.utils
|
||||
_ffmpegProcess?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user