feat(65): F10 saves a clip; the gear drawer owns the three settings
Slice 4. 607 green, 0 warnings. Clips remain **off by default** and, while off, the ring is never constructed — so no reader thread and no encoder output exist until someone asks for the feature. - **F10** (`HotkeyId.SaveClip`, default F10, remappable in the hotkey dialog) snapshots the ring, writes the file, prunes to the retention count and toasts the name. Empty ring → "nothing to clip yet" instead of a zero-byte file; clips off → says so and points at Settings. - **Settings**: Enable, length (10 or 15, default 10), keep (3/4/5, default 3). Persisted in the layout store and **coerced on load** — a bad stored value falls back to a legal one, so the ring can never be sized for a length the UI cannot show. - The ring is sized from length × tier bitrate × 1.25 and rebuilt if either changes; it is created at session start and dropped at stop, so memory is only spent while a show is running. - The drawer states the boundary in the creator's terms: "A capture, not an editor: for trimming, captions or a library, use a video editor." Still to come: the replay layer itself (play once then hide in Live, loop the kept clips elsewhere).
This commit is contained in:
@@ -164,6 +164,7 @@ public partial class MainViewModel : ViewModelBase
|
||||
_liveElapsed = TimeSpan.Zero;
|
||||
_recordLength = TimeSpan.Zero;
|
||||
StartStepDownWatch();
|
||||
StartClipRing();
|
||||
|
||||
var recordStarted = false;
|
||||
if (alsoRecord)
|
||||
@@ -407,6 +408,7 @@ public partial class MainViewModel : ViewModelBase
|
||||
{
|
||||
CancelStreamRecovery(); // a stop wins over a relaunch in flight
|
||||
StopStepDownWatch();
|
||||
StopClipRing();
|
||||
RemoveAttentionAlert("stream-recovery");
|
||||
var wasLive = IsLive;
|
||||
var wasRecording = IsRecording;
|
||||
@@ -516,6 +518,8 @@ public partial class MainViewModel : ViewModelBase
|
||||
BitrateKbps = (int)Math.Round(quality.Bitrate * 1000),
|
||||
// Same derivation as the mixer's StartLive call: a dev instance serves its own pipe.
|
||||
AudioPipeName = InstanceProfile.AudioPipeName(EncoderOptions.DefaultAudioPipeName),
|
||||
// TASK 65: null when clips are off, which leaves ffmpeg's command line untouched.
|
||||
ClipBuffer = _clipRing,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user