using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Media.Media3D; using HelixToolkit.Wpf.SharpDX; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Windows.Media.Imaging; using System.Drawing; using System.Windows; namespace SparkClient.Views.UserControl.ViewportData.Helper; public class VideoHelper { public static void CreateVideoFromPngList(List pngEncoders, string outputPath) { string tempDirectory = Path.Combine(System.Environment.CurrentDirectory, "PngFrames"); Directory.CreateDirectory(tempDirectory); try { // Save each PNG frame to a temporary directory for (int i = 0; i < pngEncoders.Count; i++) { string framePath = Path.Combine(tempDirectory, $"frame_{i:D5}.png"); using (FileStream fs = new FileStream(framePath, FileMode.Create)) { pngEncoders[i].Save(fs); } } // Use FFmpeg to create a video from the PNG frames string ffmpegPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "ffmpeg.exe"); // Assuming ffmpeg.exe is in the same directory as the application if (!File.Exists(ffmpegPath)) { throw new FileNotFoundException("FFmpeg executable not found.", ffmpegPath); } string framePattern = Path.Combine(tempDirectory, "frame_%05d.png").Replace("\\", "/"); // Replace backslashes with forward slashes for FFmpeg compatibility string arguments = $"-framerate 30 -i \"{framePattern}\" -vf \"scale=640:480\" -c:v libx264 -pix_fmt yuv420p \"{outputPath}\""; var processInfo = new System.Diagnostics.ProcessStartInfo { FileName = ffmpegPath, Arguments = arguments, RedirectStandardOutput = true, RedirectStandardError = true, UseShellExecute = false, CreateNoWindow = true }; using (var process = System.Diagnostics.Process.Start(processInfo)) { string error = process.StandardError.ReadToEnd(); process.WaitForExit(); if (process.ExitCode != 0) { throw new Exception($"FFmpeg error: {error}"); } } var dicName = Path.GetDirectoryName(outputPath); var fileName =Path.GetFileNameWithoutExtension(outputPath); Microsoft.VisualBasic.FileIO.FileSystem.RenameFile(outputPath, fileName+".dat"); } finally { // Clean up temporary frames Directory.Delete(tempDirectory, true); } } public static async Task CreateVideoFromPngListAsync(List pngEncoders, string outputPath) { string tempDirectory = Path.Combine(Environment.CurrentDirectory, "PngFrames"); Directory.CreateDirectory(tempDirectory); await Task.Delay(1).ConfigureAwait(false); try { // Save each PNG frame to a temporary directory for (int i = 0; i < pngEncoders.Count; i++) { string framePath = Path.Combine(tempDirectory, $"frame_{i:D5}.png"); await Task.Run(() => { Application.Current.Dispatcher.Invoke(() => { using (FileStream fs = new FileStream(framePath, FileMode.Create)) { pngEncoders[i].Save(fs); } }); }).ConfigureAwait(false); } // Use FFmpeg to create a video from the PNG frames string ffmpegPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "ffmpeg.exe"); // Assuming ffmpeg.exe is in the same directory as the application if (!File.Exists(ffmpegPath)) { // throw new FileNotFoundException("", ffmpegPath); MessageBox.Show("FFmpeg executable not found.", ffmpegPath); } string framePattern = Path.Combine(tempDirectory, "frame_%05d.png").Replace("\\", "/"); // Replace backslashes with forward slashes for FFmpeg compatibility string arguments = $"-framerate 30 -i \"{framePattern}\" -vf \"scale=640:480\" -c:v libx264 -pix_fmt yuv420p \"{outputPath}\""; var processInfo = new System.Diagnostics.ProcessStartInfo { FileName = ffmpegPath, Arguments = arguments, RedirectStandardOutput = true, RedirectStandardError = true, UseShellExecute = false, CreateNoWindow = true }; using (var process = System.Diagnostics.Process.Start(processInfo)) { string error = await process.StandardError.ReadToEndAsync().ConfigureAwait(false); process.WaitForExit(); if (process.ExitCode != 0) { throw new Exception($"FFmpeg error: {error}"); } } // var dicName = Path.GetDirectoryName(outputPath); // var fileName = Path.GetFileNameWithoutExtension(outputPath); // Microsoft.VisualBasic.FileIO.FileSystem.RenameFile(outputPath, fileName + ".dat"); } finally { // Clean up temporary frames Directory.Delete(tempDirectory, true); } } public static void ConvertMp4ToDat(string inputFilePath, string outputFilePath) { string ffmpegPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "ffmpeg.exe"); // Assuming ffmpeg.exe is in the same directory as the application if (!File.Exists(ffmpegPath)) { throw new FileNotFoundException("FFmpeg executable not found.", ffmpegPath); } ProcessStartInfo startInfo = new ProcessStartInfo { FileName = ffmpegPath, Arguments = $"-i \"{outputFilePath}\" \"{inputFilePath}\"", RedirectStandardOutput = true, RedirectStandardError = true, UseShellExecute = false, CreateNoWindow = true }; File.Delete(outputFilePath); } public static async Task> StartGenerationAndRotation(Viewport3DX viewport) { var genPngTask = Task.Run(() => GenPng(viewport)); var rotateModelTask = Task.Run(() => RotateModel()); await Task.WhenAll(genPngTask, rotateModelTask); return genPngTask.Result; } public static List GenPng(Viewport3DX viewport) { List result = new List(); for (int i = 0; i < 21000; i+=50) { // 使用 Dispatcher.Invoke 来确保 UI 操作在主线程中执行 var encoder = Application.Current.Dispatcher.Invoke(() => CutPng(viewport)); result.Add(encoder); Thread.Sleep(20); // 模拟处理过程 } return result; } public static async void RotateModel() { await Application.Current.Dispatcher.InvokeAsync(() => { var center = ViewportManager.ModelBounds.Center; var maxDimension = ViewportManager.ModelBounds.Size.Length(); var distance = maxDimension *1.2; // 调整相机到模型的距离,保证视野范围内 var camera = ViewportManager.GetViewport3D().Camera as HelixToolkit.Wpf.SharpDX.OrthographicCamera; camera.Position = new Point3D(center.X, center.Y, center.Z + distance); // 从前面看,Z轴正方向 camera.Width = ViewportManager.calCameraWidth(); camera.UpDirection = new Vector3D(0, -1, 0); camera.LookDirection = new Vector3D(center.X - camera.Position.X, center.Y - camera.Position.Y, center.Z - camera.Position.Z); ViewportHelperPro.RotateModel(new Vector3D(0,-1,0)); }); await Task.Delay(7000); await Application.Current.Dispatcher.InvokeAsync(() => { ViewportHelperPro.RotateModel(new Vector3D(-1, 0, 0)); }); } public static PngBitmapEncoder CutPng(Viewport3DX viewport) { int width = (int)viewport.ActualWidth; int height = (int)viewport.ActualHeight; var renderTargetBitmap = new RenderTargetBitmap(width, height, 96, 96, PixelFormats.Pbgra32); renderTargetBitmap.Render(viewport); var encoder = new PngBitmapEncoder(); encoder.Frames.Add(BitmapFrame.Create(renderTargetBitmap)); return encoder; } }