Files
kniip/.cursor/plans/screencasts_mp4_gif_68933a82.plan.md
2026-11-07 12:19:45 +01:00

6.0 KiB

name, overview, todos, isProject
name overview todos isProject
Screencasts MP4 GIF Knip+ krijgt een eigen schermopname: gebied kiezen via tray of sneltoets, opnemen met geluid, muisaanwijzer en klikmarkering, daarna begin en einde inkorten en opslaan als MP4 of GIF.
id content status
spike ScreenRecorderLib toevoegen, TFM naar windows10.0.19041.0, en controleren dat single-file publish met opname werkt completed
id content status
hotkey-tray GlobalHotkey + tray-item + StartRecording/StopRecording in App completed
id content status
selector RegionSelectorWindow (slepen, venster, heel scherm) en RegionFrameWindow completed
id content status
toolbar-session RecordingToolbar en RecordingSession (audio, cursor, klikken, pauze, aftellen) completed
id content status
screencast-window ScreencastWindow met preview en bereikschuif voor inkorten completed
id content status
export VideoExporter (MP4 trim) en GifEncoder (frames via GetThumbnailsAsync, quantisatie, delta-frames) completed
id content status
settings AppSettings-velden en sectie Screencasts in SettingsWindow completed
id content status
build build.ps1/installer bijwerken en eind-tot-eind testen completed
false

Screencasts (MP4 en GIF) in Knip+

Aanpak

Er komt één opnamepad: Knip+ neemt altijd eerst op als MP4 in een tijdelijk bestand (met geluid als dat aan staat). Pas na het stoppen kies je in een nabewerkingsvenster het bereik en het formaat. Zo hoef je vooraf niets te beslissen, en GIF en MP4 delen dezelfde opname en dezelfde inkortfunctie.

flowchart LR
    trigger["Tray of sneltoets"] --> selector[RegionSelectorWindow]
    selector --> toolbar["RecordingToolbar: start, pauze, stop"]
    toolbar --> session["RecordingSession: tijdelijke MP4"]
    session --> editor["ScreencastWindow: voorbeeld en inkorten"]
    editor --> mp4["VideoExporter: MP4"]
    editor --> gif["GifEncoder: GIF"]

Techniek

  • Opnemen: NuGet-pakket ScreenRecorderLib (MIT, gebruikt Windows Media Foundation, H.264/AAC). Dat regelt in één keer het gekozen gebied, de muisaanwijzer, de klikmarkering en het mixen van systeemgeluid en microfoon. Er is dan geen ffmpeg nodig.
  • Inkorten naar MP4: Windows.Media.Editing.MediaComposition met TrimTimeFromStart en TrimTimeFromEnd, en daarna RenderToFileAsync. Daarvoor gaat het target framework in src/KnipPlus/KnipPlus.csproj van net8.0-windows naar net8.0-windows10.0.19041.0.
  • GIF: frames uit de MP4 halen met MediaComposition.GetThumbnailsAsync, met een instelbare fps (standaard 10) en maximale breedte. Daarna codeert een eigen GifEncoder ze: per frame een palet van 256 kleuren (octree-quantisatie), framevertraging, oneindig herhalen (NETSCAPE-extensie) en ongewijzigde pixels transparant maken, zodat het bestand klein blijft. De ingebouwde GifBitmapEncoder van WPF kent geen framevertraging of herhalen en is daarom niet bruikbaar.
  • Risico bij single-file publish: ScreenRecorderLib is een C++/CLI-assembly en die kan niet vanuit de bundel geladen worden. Daarom komt -p:IncludeAllContentForSelfExtract=true in build.ps1 (of als fallback publiceren als map via de installer). Dit test ik direct als eerste stap.

Nieuwe bestanden (src/KnipPlus/Recording/)

  • RegionSelectorWindow.xaml(.cs): een transparante overlay over alle schermen. Je sleept een rechthoek, klikt op een venster om dat te kiezen, of drukt op Enter voor het hele scherm. De selectie houdt rekening met DPI per monitor.
  • RegionFrameWindow.cs: een rand rond het opnamegebied die klikken doorlaat en buiten het gebied valt, zodat hij niet in de opname komt.
  • RecordingToolbar.xaml(.cs): een kleine knoppenbalk die altijd bovenop blijft, naast het gebied. Daarop staan schakelaars voor systeemgeluid en microfoon, en knoppen voor start (met 3 seconden aftellen), pauze en stop, plus de verstreken tijd.
  • RecordingSession.cs: een wrapper om ScreenRecorderLib.Recorder (opties, status, fouten) die een tijdelijk MP4-pad oplevert in %TEMP%\KnipPlus.
  • ScreencastWindow.xaml(.cs): voorbeeld van de opname via MediaElement, een bereikschuif met begin- en eindgreep en afspelen binnen het bereik. Verder knoppen "Opslaan als MP4", "Opslaan als GIF" (met fps en breedte) en "Kopiëren" (het bestand als FileDropList op het klembord). Bij het sluiten wordt het tijdelijke bestand opgeruimd.
  • VideoExporter.cs: MP4 inkorten en renderen.
  • GifEncoder.cs: GIF-encoder met quantisatie en het overslaan van ongewijzigde pixels per frame.

Wijzigingen in bestaande bestanden

  • src/KnipPlus/Infrastructure/TrayIcon.cs: menu-item "Screencast opnemen..." en een event RecordRequested. Tijdens een opname wordt dit "Opname stoppen".
  • src/KnipPlus/Infrastructure/GlobalHotkey.cs (nieuw): RegisterHotKey via een verborgen HwndSource. Dezelfde sneltoets start of stopt de opname (standaard Ctrl + Shift + R). Als de sneltoets al bezet is, verschijnt er een ballonmelding.
  • src/KnipPlus/Interop/NativeMethods.cs: RegisterHotKey, UnregisterHotKey en WindowFromPoint (voor het kiezen van een venster).
  • src/KnipPlus/App.xaml.cs: StartRecording() en StopRecording(). Er draait maximaal één opname tegelijk. Tijdens de opname staat Capture.Paused aan, zodat er geen knipsels opgepikt worden.
  • src/KnipPlus/Settings/AppSettings.cs: RecordHotkey, RecordSystemAudio, RecordMicrophone, RecordShowCursor, RecordHighlightClicks, RecordFps (30), GifFps (10), GifMaxWidth (800) en LastVideoFolder (standaard Video's\Knip+).
  • src/KnipPlus/Settings/SettingsWindow.xaml en de bijbehorende .cs: een sectie "Screencasts" met de sneltoets, standaardinstellingen voor geluid, muisaanwijzer en klikken, en GIF-instellingen.
  • installer/KnipPlus.iss: alleen aanpassen als de fallback (publiceren als map) nodig blijkt.