From f1cd9e25e5c2a29d9ae996fd6a3e384323a7760e Mon Sep 17 00:00:00 2001 From: ww-rm Date: Fri, 11 Jul 2025 16:52:50 +0800 Subject: [PATCH 1/5] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=BD=BF=E7=94=A8FFmpeg?= =?UTF-8?q?=E5=AF=BC=E5=87=BA=E6=97=B6=E7=9A=84=E5=8D=A1=E6=AD=BB=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Spine/Exporters/FFmpegVideoExporter.cs | 2 +- SpineViewer/ViewModels/Exporters/BaseExporterViewModel.cs | 1 - .../ViewModels/Exporters/CustomFFmpegExporterViewModel.cs | 4 ++++ .../ViewModels/Exporters/FFmpegVideoExporterViewModel.cs | 6 ++++++ .../ViewModels/Exporters/FrameSequenceExporterViewModel.cs | 4 ++++ 5 files changed, 15 insertions(+), 2 deletions(-) diff --git a/Spine/Exporters/FFmpegVideoExporter.cs b/Spine/Exporters/FFmpegVideoExporter.cs index 5bc2346..744b899 100644 --- a/Spine/Exporters/FFmpegVideoExporter.cs +++ b/Spine/Exporters/FFmpegVideoExporter.cs @@ -62,7 +62,7 @@ namespace Spine.Exporters /// protected override SFMLImageVideoFrame GetFrame(SpineObject[] spines) { - // XXX: 不知道为什么用 FFmpeg 必须临时创建 RenderTexture, 否则无法正常渲染 + // BUG: 不知道为什么用 FFmpeg 必须临时创建 RenderTexture, 否则无法正常渲染 using var tex = new RenderTexture(_renderTexture.Size.X, _renderTexture.Size.Y); using var view = _renderTexture.GetView(); tex.SetView(view); diff --git a/SpineViewer/ViewModels/Exporters/BaseExporterViewModel.cs b/SpineViewer/ViewModels/Exporters/BaseExporterViewModel.cs index c078c48..66fb38b 100644 --- a/SpineViewer/ViewModels/Exporters/BaseExporterViewModel.cs +++ b/SpineViewer/ViewModels/Exporters/BaseExporterViewModel.cs @@ -142,7 +142,6 @@ namespace SpineViewer.ViewModels.Exporters { if (!Export_CanExecute(args)) return; Export(args.Cast().ToArray()); - // XXX: 导出途中应该停掉渲染好一些, 让性能专注在导出上 } private bool Export_CanExecute(IList? args) diff --git a/SpineViewer/ViewModels/Exporters/CustomFFmpegExporterViewModel.cs b/SpineViewer/ViewModels/Exporters/CustomFFmpegExporterViewModel.cs index 2b5caa9..c31f8c2 100644 --- a/SpineViewer/ViewModels/Exporters/CustomFFmpegExporterViewModel.cs +++ b/SpineViewer/ViewModels/Exporters/CustomFFmpegExporterViewModel.cs @@ -83,6 +83,8 @@ namespace SpineViewer.ViewModels.Exporters exporter.Rotation = view.Rotation; } + _vmMain.SFMLRendererViewModel.StopRender(); + if (_exportSingle) { var filename = $"ffmpeg_{timestamp}_{Guid.NewGuid().ToString()[..6]}_{_fps}{FormatSuffix}"; @@ -168,6 +170,8 @@ namespace SpineViewer.ViewModels.Exporters } _vmMain.ProgressState = System.Windows.Shell.TaskbarItemProgressState.None; } + + _vmMain.SFMLRendererViewModel.StartRender(); } } } diff --git a/SpineViewer/ViewModels/Exporters/FFmpegVideoExporterViewModel.cs b/SpineViewer/ViewModels/Exporters/FFmpegVideoExporterViewModel.cs index d206531..779c134 100644 --- a/SpineViewer/ViewModels/Exporters/FFmpegVideoExporterViewModel.cs +++ b/SpineViewer/ViewModels/Exporters/FFmpegVideoExporterViewModel.cs @@ -68,6 +68,10 @@ namespace SpineViewer.ViewModels.Exporters exporter.Rotation = view.Rotation; } + // BUG: FFmpeg 导出时对 RenderTexture 的频繁资源申请释放似乎使 SFML 库内部出现问题, 会卡死所有使用 SFML 的地方, 包括渲染线程 + // 所以临时把渲染线程停掉, 只让此处使用 SFML 资源, 这个问题或许和多个线程同时使用渲染资源有关 + _vmMain.SFMLRendererViewModel.StopRender(); + if (_exportSingle) { var filename = $"video_{timestamp}_{Guid.NewGuid().ToString()[..6]}_{_fps}{FormatSuffix}"; @@ -153,6 +157,8 @@ namespace SpineViewer.ViewModels.Exporters } _vmMain.ProgressState = System.Windows.Shell.TaskbarItemProgressState.None; } + + _vmMain.SFMLRendererViewModel.StartRender(); } } } diff --git a/SpineViewer/ViewModels/Exporters/FrameSequenceExporterViewModel.cs b/SpineViewer/ViewModels/Exporters/FrameSequenceExporterViewModel.cs index add965f..fab8999 100644 --- a/SpineViewer/ViewModels/Exporters/FrameSequenceExporterViewModel.cs +++ b/SpineViewer/ViewModels/Exporters/FrameSequenceExporterViewModel.cs @@ -47,6 +47,8 @@ namespace SpineViewer.ViewModels.Exporters exporter.Rotation = view.Rotation; } + _vmMain.SFMLRendererViewModel.StopRender(); + if (_exportSingle) { var folderName = $"frames_{timestamp}_{Guid.NewGuid().ToString()[..6]}_{_fps}"; @@ -132,6 +134,8 @@ namespace SpineViewer.ViewModels.Exporters } _vmMain.ProgressState = System.Windows.Shell.TaskbarItemProgressState.None; } + + _vmMain.SFMLRendererViewModel.StartRender(); } } } From 65c1012205d1041dab8ceee4ec9c596d0ff5d8a6 Mon Sep 17 00:00:00 2001 From: ww-rm Date: Fri, 11 Jul 2025 16:56:01 +0800 Subject: [PATCH 2/5] =?UTF-8?q?=E6=9B=B4=E6=94=B9=E6=8F=90=E7=A4=BA?= =?UTF-8?q?=E6=96=87=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- SpineViewer/ViewModels/MainWindow/SpineObjectListViewModel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SpineViewer/ViewModels/MainWindow/SpineObjectListViewModel.cs b/SpineViewer/ViewModels/MainWindow/SpineObjectListViewModel.cs index d058952..eafdbe9 100644 --- a/SpineViewer/ViewModels/MainWindow/SpineObjectListViewModel.cs +++ b/SpineViewer/ViewModels/MainWindow/SpineObjectListViewModel.cs @@ -101,7 +101,7 @@ namespace SpineViewer.ViewModels.MainWindow private void AddSpineObject_Execute() { - MessagePopupService.Info("Not Implemented, try next version :)"); + MessagePopupService.Info("Not Implemented, please drag files into here or add them from clipboard :)"); } /// From f1c013bd82dc84b9bc8f38f8a1118ef9578d561f Mon Sep 17 00:00:00 2001 From: ww-rm Date: Fri, 11 Jul 2025 23:28:36 +0800 Subject: [PATCH 3/5] =?UTF-8?q?=E5=A2=9E=E5=8A=A0webp=E6=A0=BC=E5=BC=8F?= =?UTF-8?q?=E6=97=A0=E6=8D=9F=E5=8F=82=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Spine/Exporters/FFmpegVideoExporter.cs | 11 +++++++++-- SpineViewer/Resources/Strings/en.xaml | 2 ++ SpineViewer/Resources/Strings/ja.xaml | 2 ++ SpineViewer/Resources/Strings/zh.xaml | 4 +++- .../Exporters/FFmpegVideoExporterViewModel.cs | 4 ++++ .../ExporterDialogs/FFmpegVideoExporterDialog.xaml | 11 ++++++++--- 6 files changed, 28 insertions(+), 6 deletions(-) diff --git a/Spine/Exporters/FFmpegVideoExporter.cs b/Spine/Exporters/FFmpegVideoExporter.cs index 744b899..cce74c7 100644 --- a/Spine/Exporters/FFmpegVideoExporter.cs +++ b/Spine/Exporters/FFmpegVideoExporter.cs @@ -51,6 +51,12 @@ namespace Spine.Exporters public int Quality { get => _quality; set => _quality = Math.Clamp(value, 0, 100); } private int _quality = 75; + /// + /// 无损压缩 (Webp) + /// + public bool Lossless { get => _lossless; set => _lossless = value; } + private bool _lossless = false; + /// /// CRF /// @@ -62,7 +68,8 @@ namespace Spine.Exporters /// protected override SFMLImageVideoFrame GetFrame(SpineObject[] spines) { - // BUG: 不知道为什么用 FFmpeg 必须临时创建 RenderTexture, 否则无法正常渲染 + // BUG: 也许和 SFML 多线程或者 FFmpeg 调用有关, 当渲染线程也在运行的时候此处并行渲染会导致和 SFML 有关的内容都卡死 + // 不知道为什么用 FFmpeg 必须临时创建 RenderTexture, 否则无法正常渲染, 会导致画面帧丢失 using var tex = new RenderTexture(_renderTexture.Size.X, _renderTexture.Size.Y); using var view = _renderTexture.GetView(); tex.SetView(view); @@ -112,7 +119,7 @@ namespace Spine.Exporters private void SetWebpOptions(FFMpegArgumentOptions options) { - var customArgs = $"-vf unpremultiply=inplace=1 -quality {_quality} -loop {(_loop ? 0 : 1)}"; + var customArgs = $"-vf unpremultiply=inplace=1 -quality {_quality} -loop {(_loop ? 0 : 1)} -lossless {(_lossless ? 1 : 0)}"; options.ForceFormat("webp").WithVideoCodec("libwebp_anim").ForcePixelFormat("yuva420p") .WithCustomArgument(customArgs); } diff --git a/SpineViewer/Resources/Strings/en.xaml b/SpineViewer/Resources/Strings/en.xaml index cadde50..34e67ef 100644 --- a/SpineViewer/Resources/Strings/en.xaml +++ b/SpineViewer/Resources/Strings/en.xaml @@ -174,6 +174,8 @@ Loop animation; only effective for GIF/WebP formats Quality Parameter Range 0–100; higher is better; only for WebP format + Lossless Compression + Lossless compression. Ignores the quality parameter and only applies to WebP format. CRF Parameter Range 0–63; lower is higher quality; only for MP4/WebM/MKV formats diff --git a/SpineViewer/Resources/Strings/ja.xaml b/SpineViewer/Resources/Strings/ja.xaml index 83a119a..f3c1b3d 100644 --- a/SpineViewer/Resources/Strings/ja.xaml +++ b/SpineViewer/Resources/Strings/ja.xaml @@ -174,6 +174,8 @@ アニメーションをループ再生するか。Gif/Webp形式のみ有効です 品質パラメーター 品質パラメーター。値の範囲は0-100。数値が大きいほど品質が高くなります。Webp形式のみ有効です + 可逆圧縮 + 可逆圧縮を行います。品質パラメータは無視され、WebP形式にのみ適用されます。 CRFパラメーター CRFパラメーター。値の範囲は0-63。数値が小さいほど品質が高くなります。Mp4/Webm/Mkv形式のみ有効です diff --git a/SpineViewer/Resources/Strings/zh.xaml b/SpineViewer/Resources/Strings/zh.xaml index 983b8e4..53c90de 100644 --- a/SpineViewer/Resources/Strings/zh.xaml +++ b/SpineViewer/Resources/Strings/zh.xaml @@ -173,7 +173,9 @@ 循环播放 动图是否循环播放,仅对 Gif/Webp 格式生效 质量参数 - 质量参数,取值范围 0-100,越高质量越好 仅对 Webp 格式生效 + 质量参数,取值范围 0-100,越高质量越好, 仅对 Webp 格式生效 + 无损压缩 + 无损压缩, 会忽略质量参数, 仅对 Webp 格式生效 CRF 参数 CRF 参数,取值范围 0-63,越小质量越高,仅对 Mp4/Webm/Mkv 格式生效 diff --git a/SpineViewer/ViewModels/Exporters/FFmpegVideoExporterViewModel.cs b/SpineViewer/ViewModels/Exporters/FFmpegVideoExporterViewModel.cs index 779c134..37471ff 100644 --- a/SpineViewer/ViewModels/Exporters/FFmpegVideoExporterViewModel.cs +++ b/SpineViewer/ViewModels/Exporters/FFmpegVideoExporterViewModel.cs @@ -29,6 +29,9 @@ namespace SpineViewer.ViewModels.Exporters public int Quality { get => _quality; set => SetProperty(ref _quality, Math.Clamp(value, 0, 100)); } protected int _quality = 75; + public bool Lossless { get => _lossless; set => SetProperty(ref _lossless, value); } + protected bool _lossless = false; + public int Crf { get => _crf; set => SetProperty(ref _crf, Math.Clamp(value, 0, 63)); } protected int _crf = 23; @@ -55,6 +58,7 @@ namespace SpineViewer.ViewModels.Exporters Format = _format, Loop = _loop, Quality = _quality, + Lossless = _lossless, Crf = _crf }; diff --git a/SpineViewer/Views/ExporterDialogs/FFmpegVideoExporterDialog.xaml b/SpineViewer/Views/ExporterDialogs/FFmpegVideoExporterDialog.xaml index 4882b27..394d3a1 100644 --- a/SpineViewer/Views/ExporterDialogs/FFmpegVideoExporterDialog.xaml +++ b/SpineViewer/Views/ExporterDialogs/FFmpegVideoExporterDialog.xaml @@ -9,7 +9,7 @@ mc:Ignorable="d" Title="{DynamicResource Str_FFmpegVideoExporterTitle}" Width="450" - Height="550" + Height="580" ShowInTaskbar="False" WindowStartupLocation="CenterOwner"> @@ -66,6 +66,7 @@ + @@ -138,9 +139,13 @@