From e4f4b7f5eabb768b3a104bee9c4cfebb15e298f7 Mon Sep 17 00:00:00 2001 From: FaulMit <48646918+FaulMit@users.noreply.github.com> Date: Mon, 3 Aug 2026 16:59:01 +0200 Subject: [PATCH] Release Captail 0.1.6 --- CHANGELOG.md | 11 ++++ src/Captail/App.xaml.cs | 82 ++++++++++++++++++++++++++- src/Captail/Captail.csproj | 2 +- src/Captail/ClipEditorWindow.xaml | 73 +++++++++++++++++++++++- src/Captail/ClipEditorWindow.xaml.cs | 30 +++++++++- src/Captail/FfmpegAdapter.cs | 50 +++++++++++++--- src/Captail/Languages/Strings.en.xaml | 5 ++ src/Captail/Languages/Strings.ru.xaml | 5 ++ src/Captail/ObsReplayEngine.cs | 15 ++++- src/Captail/ReplayLibrary.cs | 4 ++ src/Captail/SettingsWindow.xaml.cs | 5 +- 11 files changed, 264 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 976babd..214513d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,17 @@ All notable user-facing changes are documented here. ## [Unreleased] +## [0.1.6] - 2026-08-03 + +### Added + +- Multi-track replays can now mix all enabled audio tracks into one playback-friendly track when saving or overwriting a trimmed clip. Video remains untouched; only audio is re-encoded. + +### Fixed + +- Settings selected in the window now remain visible when the recording pipeline rejects a change, making it possible to correct the failing option without re-entering resolution, audio, and other pending choices. +- NVIDIA HEVC encoding no longer requests B-frames, fixing encoder startup on hardware such as the GeForce GTX 1080 where HEVC B-frames are unsupported. + ## [0.1.5] - 2026-08-02 ### Fixed diff --git a/src/Captail/App.xaml.cs b/src/Captail/App.xaml.cs index e2d18ca..45e3ff1 100644 --- a/src/Captail/App.xaml.cs +++ b/src/Captail/App.xaml.cs @@ -86,6 +86,12 @@ protected override async void OnStartup(StartupEventArgs e) StringComparison.OrdinalIgnoreCase)) ?["--qa-clip-editor=".Length..]; bool clipEditorTest = !string.IsNullOrWhiteSpace(clipEditorTestPath); + string? audioMixTestPath = e.Args + .FirstOrDefault(argument => argument.StartsWith( + "--qa-audio-mix=", + StringComparison.OrdinalIgnoreCase)) + ?["--qa-audio-mix=".Length..]; + bool audioMixTest = !string.IsNullOrWhiteSpace(audioMixTestPath); string? previewGeometryTestPath = e.Args .FirstOrDefault(argument => argument.StartsWith( "--qa-preview-geometry=", @@ -104,6 +110,7 @@ protected override async void OnStartup(StartupEventArgs e) const bool replaySegmentsTest = false; const bool updateCheckTest = false; const bool clipEditorTest = false; + const bool audioMixTest = false; const bool previewGeometryTest = false; #endif bool backgroundLaunch = e.Args.Contains( @@ -118,7 +125,7 @@ protected override async void OnStartup(StartupEventArgs e) shutdownExisting, _uiOnly || faultTest || codecTest || capabilityModelTest || gameCaptureTest || replaySegmentsTest || updateCheckTest || - clipEditorTest || previewGeometryTest)) + clipEditorTest || audioMixTest || previewGeometryTest)) { Shutdown(); return; @@ -185,6 +192,11 @@ await _updateService.CheckAsync( await RunClipEditorTestAsync(clipEditorTestPath!); return; } + if (audioMixTest) + { + await RunAudioMixTestAsync(audioMixTestPath!); + return; + } if (previewGeometryTest) { await RunPreviewGeometryTestAsync(previewGeometryTestPath!); @@ -251,6 +263,71 @@ await TryStartPipelineAsync(showError: true)) } #if DEBUG + private async Task RunAudioMixTestAsync(string path) + { + string fullPath = Path.GetFullPath(path); + if (!File.Exists(fullPath)) + throw new FileNotFoundException("QA replay does not exist.", fullPath); + + string destination = Path.Combine( + Path.GetTempPath(), + $"captail_audio_mix_{Guid.NewGuid():N}{Path.GetExtension(fullPath)}"); + try + { + var ffmpeg = new FfmpegAdapter(); + TimeSpan duration = await ffmpeg.ReadDurationAsync(fullPath); + IReadOnlyList sourceTracks = + await ffmpeg.ReadAudioTracksAsync(fullPath); + VideoStreamInfo? sourceVideo = await ffmpeg.ReadVideoInfoAsync(fullPath); + if (sourceTracks.Count < 2 || sourceVideo is null) + throw new InvalidOperationException( + "QA replay requires video and at least two audio tracks."); + + await ffmpeg.TrimCopyAsync( + fullPath, + destination, + TimeSpan.Zero, + duration, + sourceTracks.Select(track => track.StreamIndex).ToArray(), + mergeAudioTracks: true); + + IReadOnlyList mixedTracks = + await ffmpeg.ReadAudioTracksAsync(destination); + VideoStreamInfo? mixedVideo = await ffmpeg.ReadVideoInfoAsync(destination); + bool passed = mixedTracks.Count == 1 && + mixedVideo is not null && + mixedVideo.Codec.Equals( + sourceVideo.Codec, + StringComparison.OrdinalIgnoreCase) && + mixedVideo.Width == sourceVideo.Width && + mixedVideo.Height == sourceVideo.Height; + Log.Write( + $"AUDIO_MIX_TEST {(passed ? "PASS" : "FAIL")}: " + + $"sourceTracks={sourceTracks.Count}, mixedTracks={mixedTracks.Count}, " + + $"video={sourceVideo.Codec}/{mixedVideo?.Codec} " + + $"{sourceVideo.Width}x{sourceVideo.Height}/" + + $"{mixedVideo?.Width}x{mixedVideo?.Height}"); + Shutdown(passed ? 0 : 16); + } + catch (Exception exception) + { + Log.Write($"AUDIO_MIX_TEST FAIL: {exception}"); + Shutdown(16); + } + finally + { + try + { + if (File.Exists(destination)) + File.Delete(destination); + } + catch (Exception exception) + { + Log.Write($"Audio mix QA cleanup failed: {exception.Message}"); + } + } + } + private async Task RunClipEditorTestAsync(string path) { string fullPath = Path.GetFullPath(path); @@ -373,6 +450,9 @@ private void RunCapabilityModelTest() invalidConfig.Codec == "h264" && invalidConfig.SystemAudioVolume == 100 && invalidConfig.Hotkey == "Ctrl+Shift+F10" && + ObsReplayEngine.RecommendedNvencBFrames("hevc", true) == 0 && + ObsReplayEngine.RecommendedNvencBFrames("h264", true) == 2 && + ObsReplayEngine.RecommendedNvencBFrames("h264", false) == 0 && invalidConfig.PipelineEquals(hotkeyOnlyChange) && !invalidConfig.PipelineEquals(pipelineChange); Log.Write( diff --git a/src/Captail/Captail.csproj b/src/Captail/Captail.csproj index 45f84fd..4dac14b 100644 --- a/src/Captail/Captail.csproj +++ b/src/Captail/Captail.csproj @@ -14,7 +14,7 @@ Captail Captail Assets\Captail.ico - 0.1.5 + 0.1.6 true $(DefineConstants);MICROSOFT_STORE diff --git a/src/Captail/ClipEditorWindow.xaml b/src/Captail/ClipEditorWindow.xaml index 2891295..6578585 100644 --- a/src/Captail/ClipEditorWindow.xaml +++ b/src/Captail/ClipEditorWindow.xaml @@ -11,6 +11,63 @@ Foreground="{StaticResource TextPrimaryBrush}" FontFamily="{StaticResource FontUi}" PreviewKeyDown="Window_PreviewKeyDown"> + + + @@ -234,6 +291,19 @@ Content="{DynamicResource L.Library.Overwrite}" Style="{StaticResource SubtleButton}" Padding="16,9" Click="RequestOverwrite_Click"/> + + + + + +