-
Notifications
You must be signed in to change notification settings - Fork 0
/
VideoConverter.cs
166 lines (144 loc) · 6.67 KB
/
VideoConverter.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
using System.Diagnostics;
namespace VideoConverter
{
public static class VideoConverter
{
public static void ConvertToHevc(
string inputFile,
string? outputFile = null,
int crf = 28,
string preset = "fast",
string audioCodec = "aac",
string? resolution = null,
string encoder = "nvenc",
string? bitrate = null)
{
Console.WriteLine($"调试信息:分辨率参数 = {resolution ?? "未设置"}");
ValidateInputParameters(inputFile, crf, preset, audioCodec, resolution, encoder);
outputFile ??= GenerateOutputFilename(inputFile);
Console.WriteLine($"开始转换:{inputFile} => {outputFile}");
var resolutionOption = string.IsNullOrEmpty(resolution) ? "" : $"-vf scale=-2:{resolution}";
var ffmpegCommand = CreateFfmpegCommand(inputFile, outputFile, crf, preset, audioCodec, resolutionOption, encoder, bitrate);
ExecuteFfmpegCommand(ffmpegCommand);
}
private static void ValidateInputParameters(string inputFile, int crf, string preset, string audioCodec, string? resolution, string encoder)
{
ValidateInputFile(inputFile);
ValidateCrf(crf);
ValidatePreset(preset, encoder);
ValidateAudioCodec(audioCodec);
ValidateResolution(resolution);
}
private static void ValidateInputFile(string inputFile)
{
if (string.IsNullOrWhiteSpace(inputFile))
{
throw new ArgumentException("输入文件路径不能为空", nameof(inputFile));
}
if (!File.Exists(inputFile))
{
throw new FileNotFoundException($"未找到输入文件: {inputFile}");
}
}
private static void ValidateCrf(int crf)
{
if (crf < Constants.MinCrf || crf > Constants.MaxCrf)
{
throw new ArgumentOutOfRangeException(nameof(crf), $"CRF 值必须在 {Constants.MinCrf} 到 {Constants.MaxCrf} 之间");
}
}
private static void ValidatePreset(string preset, string encoder)
{
if (encoder != "libx265" && encoder != "nvenc")
{
throw new ArgumentException("不支持的编码器, 请使用 nvenc 或 libx265", nameof(encoder));
}
string[] validPresets = encoder == "libx265" ? Constants.ValidPresetsForLibx265 : Constants.ValidPresetsForNvenc;
if (int.TryParse(preset, out int presetIndex))
{
if (presetIndex < 0 || presetIndex >= validPresets.Length)
{
throw new ArgumentException($"无效的 preset 值: {preset}。数字应在 0 到 {validPresets.Length - 1} 之间", nameof(preset));
}
}
else if (!validPresets.Contains(preset.ToLower()))
{
throw new ArgumentException($"无效的 preset 值: {preset}。有效值为: {string.Join(", ", validPresets)} 或 0-{validPresets.Length - 1}", nameof(preset));
}
}
private static void ValidateAudioCodec(string audioCodec)
{
if (!Constants.ValidAudioCodecs.Contains(audioCodec.ToLower()))
{
throw new ArgumentException($"无效的音频编码格式: {audioCodec}。有效值为: {string.Join(", ", Constants.ValidAudioCodecs)}", nameof(audioCodec));
}
}
private static void ValidateResolution(string? resolution)
{
if (!string.IsNullOrEmpty(resolution))
{
if (!int.TryParse(resolution, out int resolutionValue) || resolutionValue <= 0)
{
throw new ArgumentException("分辨率必须是正整数", nameof(resolution));
}
}
}
private static ProcessStartInfo CreateFfmpegCommand(string inputFile, string outputFile, int crf, string preset, string audioCodec, string resolutionOption, string encoder, string? bitrate)
{
string[] validPresets = encoder == "libx265" ? Constants.ValidPresetsForLibx265 : Constants.ValidPresetsForNvenc;
if (int.TryParse(preset, out int presetIndex))
{
preset = validPresets[presetIndex];
}
string encoderOption = encoder == "libx265" ? "-c:v libx265" : "-c:v hevc_nvenc";
string bitrateOption = string.IsNullOrEmpty(bitrate) ? "" : $"-b:v {bitrate}";
return new ProcessStartInfo
{
FileName = "ffmpeg",
Arguments = $"-y -i \"{inputFile}\" {encoderOption} -preset {preset} {resolutionOption} {bitrateOption} -crf {crf} -c:a {audioCodec} \"{outputFile}\"",
RedirectStandardError = true,
RedirectStandardOutput = true,
UseShellExecute = false,
CreateNoWindow = true
};
}
private static void ExecuteFfmpegCommand(ProcessStartInfo ffmpegCommand)
{
using var process = new Process { StartInfo = ffmpegCommand };
process.ErrorDataReceived += (sender, e) => Console.WriteLine(e.Data);
process.OutputDataReceived += (sender, e) => Console.WriteLine(e.Data);
try
{
Console.WriteLine($"执行命令: {ffmpegCommand.FileName} {ffmpegCommand.Arguments}");
process.Start();
process.BeginErrorReadLine();
process.BeginOutputReadLine();
process.WaitForExit();
if (process.ExitCode == 0)
{
Console.WriteLine($"转换成功:{ffmpegCommand.Arguments.Split('"').LastOrDefault()}");
}
else
{
throw new Exception($"转换失败,FFmpeg 返回码:{process.ExitCode}");
}
}
catch (Exception ex)
{
Console.WriteLine($"转换过程中发生错误:{ex.Message}");
throw;
}
}
private static string GenerateOutputFilename(string inputFile)
{
var random = new Random();
var chars = "abcdefghijklmnopqrstuvwxyz0123456789";
var randomString = new string(Enumerable.Repeat(chars, 6)
.Select(s => s[random.Next(s.Length)]).ToArray());
return Path.Combine(
Path.GetDirectoryName(inputFile) ?? "",
$"{Path.GetFileNameWithoutExtension(inputFile)}_h265_{randomString}{Path.GetExtension(inputFile)}"
);
}
}
}