Skip to content

Commit 1634574

Browse files
committed
抽取视频转换代码
1 parent 00c3cf0 commit 1634574

File tree

6 files changed

+343
-294
lines changed

6 files changed

+343
-294
lines changed

MaiChartManager/AppMain.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
using System.Text.Json;
44
using Windows.ApplicationModel;
55
using Windows.ApplicationModel.Activation;
6-
using MaiChartManager.Controllers.Music;
6+
using MaiChartManager.Utils;
77
using Microsoft.Web.WebView2.Core;
88
using Xabe.FFmpeg;
99

@@ -44,7 +44,7 @@ public void Run()
4444
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.ThrowException);
4545
ApplicationConfiguration.Initialize();
4646
FFmpeg.SetExecutablesPath(StaticSettings.exeDir);
47-
MovieConvertController.CheckHardwareAcceleration();
47+
VideoConvert.CheckHardwareAcceleration();
4848

4949
Directory.CreateDirectory(StaticSettings.appData);
5050
Directory.CreateDirectory(StaticSettings.tempPath);
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using MaiChartManager.Controllers.Music;
1+
using MaiChartManager.Utils;
22
using Microsoft.AspNetCore.Mvc;
33

44
namespace MaiChartManager.Controllers.App;
@@ -7,11 +7,11 @@ namespace MaiChartManager.Controllers.App;
77
[Route("MaiChartManagerServlet/[action]Api")]
88
public class AppVersionController(StaticSettings settings, ILogger<AppVersionController> logger) : ControllerBase
99
{
10-
public record AppVersionResult(string Version, int GameVersion, IapManager.LicenseStatus License, MovieConvertController.HardwareAccelerationStatus HardwareAcceleration, string H264Encoder);
10+
public record AppVersionResult(string Version, int GameVersion, IapManager.LicenseStatus License, VideoConvert.HardwareAccelerationStatus HardwareAcceleration, string H264Encoder);
1111

1212
[HttpGet]
1313
public AppVersionResult GetAppVersion()
1414
{
15-
return new AppVersionResult(Application.ProductVersion, settings.gameVersion, IapManager.License, MovieConvertController.HardwareAcceleration, MovieConvertController.H264Encoder);
15+
return new AppVersionResult(Application.ProductVersion, settings.gameVersion, IapManager.License, VideoConvert.HardwareAcceleration, VideoConvert.H264Encoder);
1616
}
1717
}
Lines changed: 35 additions & 192 deletions
Original file line numberDiff line numberDiff line change
@@ -1,98 +1,19 @@
1-
using Microsoft.AspNetCore.Mvc;
2-
using Microsoft.VisualBasic.FileIO;
3-
using Xabe.FFmpeg;
1+
using MaiChartManager.Utils;
2+
using Microsoft.AspNetCore.Mvc;
43

54
namespace MaiChartManager.Controllers.Music;
65

76
[ApiController]
87
[Route("MaiChartManagerServlet/[action]Api/{assetDir}/{id:int}")]
9-
public class MovieConvertController(StaticSettings settings, ILogger<MovieConvertController> logger) : ControllerBase
8+
public class MovieConvertController(ILogger<MovieConvertController> logger) : ControllerBase
109
{
11-
public enum HardwareAccelerationStatus
12-
{
13-
Pending,
14-
Enabled,
15-
Disabled
16-
}
17-
18-
public static HardwareAccelerationStatus HardwareAcceleration { get; private set; } = HardwareAccelerationStatus.Pending;
19-
public static string H264Encoder { get; private set; } = "libx264";
20-
21-
public static async Task CheckHardwareAcceleration()
22-
{
23-
var tmpDir = Directory.CreateTempSubdirectory();
24-
try
25-
{
26-
var blankPath = Path.Combine(tmpDir.FullName, "blank.ivf");
27-
await FFmpeg.Conversions.New()
28-
.SetOutputTime(TimeSpan.FromSeconds(2))
29-
.SetInputFormat(Format.lavfi)
30-
.AddParameter("-i color=c=black:s=720x720:r=1")
31-
.AddParameter("-c:v vp9_qsv")
32-
.UseMultiThread(true)
33-
.SetOutput(blankPath)
34-
.Start();
35-
HardwareAcceleration = HardwareAccelerationStatus.Enabled;
36-
}
37-
catch (Exception e)
38-
{
39-
Console.WriteLine(e);
40-
HardwareAcceleration = HardwareAccelerationStatus.Disabled;
41-
}
42-
43-
foreach (var encoder in ((string[]) ["h264_nvenc", "h264_qsv", "h264_vaapi", "h264_amf", "h264_mf", "h264_vulkan"]))
44-
{
45-
try
46-
{
47-
var blankPath = Path.Combine(tmpDir.FullName, $"{encoder}.mp4");
48-
await FFmpeg.Conversions.New()
49-
.SetOutputTime(TimeSpan.FromSeconds(2))
50-
.SetInputFormat(Format.lavfi)
51-
.AddParameter("-i color=c=black:s=720x720:r=1")
52-
.AddParameter($"-c:v {encoder}")
53-
.UseMultiThread(true)
54-
.SetOutput(blankPath)
55-
.Start();
56-
H264Encoder = encoder;
57-
break;
58-
}
59-
catch (Exception e)
60-
{
61-
Console.WriteLine(e);
62-
}
63-
}
64-
65-
Console.WriteLine($"H264 encoder: {H264Encoder}");
66-
}
67-
68-
private static string Vp9Encoding => HardwareAcceleration == HardwareAccelerationStatus.Enabled ? "vp9_qsv" : "vp9";
69-
7010
public enum SetMovieEventType
7111
{
7212
Progress,
7313
Success,
7414
Error
7515
}
7616

77-
private static IConversion Concatenate(string vf, params IMediaInfo[] mediaInfos)
78-
{
79-
var conversion = FFmpeg.Conversions.New();
80-
foreach (var inputVideo in mediaInfos)
81-
{
82-
conversion.AddParameter("-i " + inputVideo.Path.Escape() + " ");
83-
}
84-
85-
conversion.AddParameter("-filter_complex \"");
86-
for (var index = 0; index < mediaInfos.Length; ++index)
87-
conversion.AddParameter($"[{index}:v]setsar=1[{index}s];");
88-
for (var index = 0; index < mediaInfos.Length; ++index)
89-
conversion.AddParameter($"[{index}s] ");
90-
conversion.AddParameter($"concat=n={mediaInfos.Length}:v=1 [v]; [v]{vf}[vout]\" -map \"[vout]\"");
91-
92-
conversion.AddParameter("-aspect 1:1");
93-
return conversion;
94-
}
95-
9617
[HttpPut]
9718
[DisableRequestSizeLimit]
9819
public async Task SetMovie(int id, [FromForm] double padding, IFormFile file, [FromForm] bool noScale, [FromForm] bool h264, [FromForm] bool yuv420p, string assetDir)
@@ -102,7 +23,7 @@ public async Task SetMovie(int id, [FromForm] double padding, IFormFile file, [F
10223
if (Path.GetExtension(file.FileName).Equals(".dat", StringComparison.InvariantCultureIgnoreCase))
10324
{
10425
var targetPath = Path.Combine(StaticSettings.StreamingAssets, assetDir, $@"MovieData\{id:000000}.dat");
105-
Directory.CreateDirectory(Path.GetDirectoryName(targetPath));
26+
Directory.CreateDirectory(Path.GetDirectoryName(targetPath)!);
10627
await using var stream = System.IO.File.Open(targetPath, FileMode.Create);
10728
await file.CopyToAsync(stream);
10829
StaticSettings.MovieDataMap[id] = targetPath;
@@ -111,139 +32,61 @@ public async Task SetMovie(int id, [FromForm] double padding, IFormFile file, [F
11132

11233
if (IapManager.License != IapManager.LicenseStatus.Active) return;
11334
Response.Headers.Append("Content-Type", "text/event-stream");
35+
11436
var tmpDir = Directory.CreateTempSubdirectory();
11537
logger.LogInformation("Temp dir: {tmpDir}", tmpDir.FullName);
116-
// Convert vp9
117-
var outVideoPath = Path.Combine(tmpDir.FullName, h264 ? "out.mp4" : "out.ivf");
38+
11839
try
11940
{
41+
// 保存上传的文件到临时目录
12042
var srcFilePath = Path.Combine(tmpDir.FullName, Path.GetFileName(file.FileName));
121-
var srcFileStream = System.IO.File.OpenWrite(srcFilePath);
122-
await file.CopyToAsync(srcFileStream);
123-
await srcFileStream.DisposeAsync();
124-
125-
var srcMedia = await FFmpeg.GetMediaInfo(srcFilePath);
126-
var firstStream = srcMedia.VideoStreams.First().SetCodec(h264 ? H264Encoder : Vp9Encoding);
127-
var conversion = FFmpeg.Conversions.New()
128-
.AddStream(firstStream);
129-
if (file.ContentType.StartsWith("image/"))
130-
{
131-
padding = 0;
132-
conversion.AddParameter("-r 1 -t 2");
133-
conversion.AddParameter("-loop 1", ParameterPosition.PreInput);
134-
}
135-
136-
if (padding is > 0 and < 0.05)
137-
{
138-
padding = 0;
139-
}
140-
141-
var vf = "";
142-
var scale = h264 ? 2160 : 1080;
143-
if (!noScale)
43+
await using (var srcFileStream = System.IO.File.OpenWrite(srcFilePath))
14444
{
145-
vf = $"scale={scale}:-1,pad={scale}:{scale}:({scale}-iw)/2:({scale}-ih)/2:black";
45+
await file.CopyToAsync(srcFileStream);
14646
}
14747

148-
if (padding < 0)
149-
{
150-
conversion.SetSeek(TimeSpan.FromSeconds(-padding));
151-
}
152-
else if (padding > 0)
153-
{
154-
var blankPath = Path.Combine(tmpDir.FullName, "blank.mp4");
155-
var blank = FFmpeg.Conversions.New()
156-
.SetOutputTime(TimeSpan.FromSeconds(padding))
157-
.SetInputFormat(Format.lavfi)
158-
.AddParameter($"-i color=c=black:s={srcMedia.VideoStreams.First().Width}x{srcMedia.VideoStreams.First().Height}:r=30")
159-
.UseMultiThread(true)
160-
.SetOutput(blankPath);
161-
logger.LogInformation("About to run FFMpeg with params: {params}", blank.Build());
162-
await blank.Start();
163-
var blankVideoInfo = await FFmpeg.GetMediaInfo(blankPath);
164-
conversion = Concatenate(vf, blankVideoInfo, srcMedia);
165-
conversion.AddParameter($"-c:v {(h264 ? "h264" : Vp9Encoding)}");
166-
}
48+
// 目标路径
49+
var targetPath = Path.Combine(StaticSettings.StreamingAssets, assetDir, $@"MovieData\{id:000000}.{(h264 ? "mp4" : "dat")}");
16750

168-
conversion
169-
.SetOutput(outVideoPath)
170-
.AddParameter("-hwaccel dxva2", ParameterPosition.PreInput)
171-
.UseMultiThread(true);
172-
if (!h264)
173-
{
174-
conversion.AddParameter("-cpu-used 5");
175-
if (yuv420p)
176-
conversion.AddParameter("-pix_fmt yuv420p");
177-
}
178-
if (!noScale && padding <= 0)
51+
// 使用工具类转换视频
52+
await VideoConvert.ConvertVideo(new VideoConvert.VideoConvertOptions
17953
{
180-
conversion.AddParameter($"-vf {vf}");
181-
}
54+
InputPath = srcFilePath,
55+
OutputPath = targetPath,
56+
NoScale = noScale,
57+
UseH264 = h264,
58+
UseYuv420p = yuv420p,
59+
Padding = padding,
60+
ContentType = file.ContentType,
61+
OnProgress = async percent =>
62+
{
63+
await Response.WriteAsync($"event: {SetMovieEventType.Progress}\ndata: {percent}\n\n");
64+
await Response.Body.FlushAsync();
65+
}
66+
});
18267

183-
logger.LogInformation("About to run FFMpeg with params: {params}", conversion.Build());
184-
conversion.OnProgress += async (sender, args) =>
185-
{
186-
await Response.WriteAsync($"event: {SetMovieEventType.Progress}\ndata: {args.Percent}\n\n");
187-
await Response.Body.FlushAsync();
188-
};
189-
await conversion.Start();
68+
StaticSettings.MovieDataMap[id] = targetPath;
69+
await Response.WriteAsync($"event: {SetMovieEventType.Success}\ndata: {SetMovieEventType.Success}\n\n");
70+
await Response.Body.FlushAsync();
19071
}
19172
catch (Exception e)
19273
{
19374
logger.LogError(e, "Failed to convert video");
19475
SentrySdk.CaptureException(e);
195-
await Response.WriteAsync($"event: {SetMovieEventType.Error}\ndata: 视频转换为 VP9 失败:{e.Message}\n\n");
196-
await Response.Body.FlushAsync();
197-
return;
198-
}
199-
200-
// Convert ivf to usm
201-
if (!System.IO.File.Exists(outVideoPath) || new FileInfo(outVideoPath).Length == 0)
202-
{
203-
await Response.WriteAsync($"event: {SetMovieEventType.Error}\ndata: 视频转换为 VP9 失败:输出文件不存在\n\n");
76+
await Response.WriteAsync($"event: {SetMovieEventType.Error}\ndata: 转换失败:{e.Message}\n\n");
20477
await Response.Body.FlushAsync();
205-
return;
20678
}
207-
208-
var outputFile = Path.Combine(tmpDir.FullName, "out.usm");
209-
if (h264)
79+
finally
21080
{
211-
outputFile = outVideoPath;
212-
}
213-
else
81+
// 清理临时目录
21482
try
21583
{
216-
WannaCRI.WannaCRI.CreateUsm(outVideoPath);
217-
if (!System.IO.File.Exists(outputFile) || new FileInfo(outputFile).Length == 0)
218-
{
219-
throw new Exception("Output file not found or empty");
220-
}
84+
tmpDir.Delete(true);
22185
}
222-
catch (Exception e)
86+
catch
22387
{
224-
logger.LogError(e, "Failed to convert ivf to usm");
225-
SentrySdk.CaptureException(e);
226-
await Response.WriteAsync($"event: {SetMovieEventType.Error}\ndata: 视频转换为 USM 失败:{e.Message}\n\n");
227-
await Response.Body.FlushAsync();
228-
return;
88+
// 忽略清理错误
22989
}
230-
231-
try
232-
{
233-
var targetPath = Path.Combine(StaticSettings.StreamingAssets, assetDir, $@"MovieData\{id:000000}.{(h264 ? "mp4" : "dat")}");
234-
Directory.CreateDirectory(Path.GetDirectoryName(targetPath));
235-
FileSystem.CopyFile(outputFile, targetPath, true);
236-
237-
StaticSettings.MovieDataMap[id] = targetPath;
238-
await Response.WriteAsync($"event: {SetMovieEventType.Success}\ndata: {SetMovieEventType.Success}\n\n");
239-
await Response.Body.FlushAsync();
240-
}
241-
catch (Exception e)
242-
{
243-
logger.LogError(e, "Failed to copy movie data");
244-
SentrySdk.CaptureException(e);
245-
await Response.WriteAsync($"event: {SetMovieEventType.Error}\ndata: 复制文件失败:{e.Message}\n\n");
246-
await Response.Body.FlushAsync();
24790
}
24891
}
24992
}

0 commit comments

Comments
 (0)