Commit Graph

21 Commits

Author SHA1 Message Date
e66734cfad [0.1.38] GPU benchmark 2026-10-18 19:25:35 -04:00
91f6cf768f [0.1.32] macOS phase 5: Mask2Former segmentation on the GPU through native Core ML 2026-10-17 21:17:00 -04:00
a1ab9f3a96 [0.1.31] macOS phase 5: PP-OCRv5 server OCR on the GPU through native Core ML 2026-10-17 21:17:00 -04:00
30cd00b6f2 [0.1.30] macOS phase 5: PP-OCRv5 mobile OCR on the GPU through native Core ML 2026-10-17 21:16:59 -04:00
2d0fb855b1 [0.1.29] macOS phase 5: SigLIP 2 labels on the GPU through native Core ML 2026-10-17 21:16:31 -04:00
a66928b223 [0.1.27] macOS phase 3: HEIC, AVIF, WebP, TIFF and RAW through ImageIO 2026-10-17 21:16:30 -04:00
4acde4d7a6 [0.1.10] Recording toolbar: a System sound button beside Mic
- System sound: on / off (red while on) turns what the computer plays on and off while
  recording, as Mic does the microphone; its state is saved as system_audio in [video].
- modules/Screen_Record: the microphone's switch is now one for either source:
  record_set_audio, record_audio_on and record_audio_failed take an Audio_Source_Kind, and the
  audio thread opens and closes each as it is turned on and off.
- record-probe -system-toggle. With -sync-test, system sound on at 3 s and off at 7 s keeps only
  the beeps at 3 and 5 s (8-12 ms from their flashes), and the track runs the whole recording.
2026-10-10 08:20:13 +01:00
3f5d1ea124 [0.1.9] Recording toolbar: a Mic button, to turn the microphone on and off mid-recording
- The toolbar's Mic: on / off (red while on) turns the microphone on and off while recording;
  its state is saved as microphone in [video] for the next recording. A microphone that cannot
  be opened turns it off again, with a toast saying why.
- modules/Screen_Record: record_set_microphone, record_microphone, record_microphone_failed. The
  audio thread opens the microphone only while it is on (so Windows shows it in use only then)
  and closes it when turned off. Record_Options.audio_track gives the MP4 a sound track whatever
  is on at the start, silent while nothing is, as one cannot be added once the header is
  written; the app always asks for it. The sources heard and the failures are kept as they
  happen, not only at the start.
- record-probe -mic-toggle: the microphone on at 3 s and off at 7 s. With system sound and with
  nothing else on, the track runs the whole recording without a break.
2026-10-10 08:16:12 +01:00
72f2754226 [0.1.8] Video capture phase 5: sound, from the computer and the microphone, in sync
- modules/Screen_Record/audio_source.jai: WASAPI, in loopback from the default output device
  (what the computer plays) and from the default input device (the microphone), asked for as
  48 kHz stereo float, which Windows converts any device to; a device that will not convert is
  taken as it is when it gives 48 kHz float. Packets carry the time they were heard. A device
  lost (unplugged, default changed) is opened again from the new default once a second.
- An audio thread mixes them onto the video's own timeline: the same time 0, less the same
  pauses (record_pause now keeps one account of time paused for both threads). A packet within
  30 ms of where its source left off follows on exactly; a source that sends nothing is silence,
  passed 200 ms late. Each 1024 samples every source has covered become an AAC frame.
- encoder.jai: an AAC stream (FFmpeg's aac, 160 kb/s) beside the video, written from the audio
  thread; both threads' packets meet in the file under a lock. The remux keeps it.
- [video] system_audio (true) and microphone (false). capture.json: Audio, AudioSources. A toast
  when a source asked for could not be opened.
- record-probe: -audio system|mic|both, the sound read back (length, level), and -sync-test,
  which flashes a white square and beeps together every 2 s, then finds both in the file.
- Measured: 20 s, beeps 0-21 ms from their flashes, no drift; both sources mixed, 5-18 ms;
  a 6 s pause cuts the same 6 s from sound and video; 3 min, 85 of 90 beeps within 3-24 ms and
  -14 ms from first to last, with 5 outliers (53-147 ms) where two beeps went missing. Tests:
  an MP4 with sound keeps its AAC stream through the remux into the library (30 checks).
2026-10-10 08:08:47 +01:00
e0eaf5642b [0.1.7] Recordings go into the library as regular MP4s, with a keyframe every 2 s
- A recording killed mid-way had wrong timestamps in players: its fragmented MP4's header gives a
  length of 0, and one cut short never gets the index a clean stop writes. modules/Screen_Record's
  record_remux now copies every recording's packets, as they are, into a regular MP4 (one header
  first with the length and every frame's index; faststart) before it goes into the library;
  should that fail, the fragmented MP4 goes in as it was.
- Keyframes were every 60 frames, which, with frames written only on change, could span minutes
  of time, and a crash could lose a fragment that long. One is now forced every 2 s of time
  (forced-idr for NVENC and libx264).
- A crash's leftover no longer claims a region at 0, 0 in its capture.json.
- record-probe -remux IN OUT. Tests: a recording cut at its last fragment, as a crash cuts it,
  goes in as a regular MP4 of the time before the cut.
- Checked on the recording that showed it: its MP4, cut mid-fragment, rewrites to 76 frames
  (5.2 s) with positive frame durations. A 12 s still recording now fragments every ~2 s, and
  the app killed 10 s into a recording leaves 8.1 s.
2026-10-10 07:54:48 +01:00
59c5b8dd62 [0.1.6] Video capture phase 4: border, toolbar, pause, discard, recovery, leftovers, [video]
- src/app/screen_record_win32.jai: while recording, a red frame just outside the region (one
  window shaped by a window region, clicks let through) and a toolbar beside it (below the
  region at its right, else above, else inside a whole screen's top right): a blinking dot,
  the time less time paused, the size, frames dropped and screen losses, and Pause/Resume,
  Stop, and Discard, confirmed in the toolbar itself ("Yes, discard / No"). GDI windows that
  never take the focus, kept out of every capture (WDA_EXCLUDEFROMCAPTURE). Buttons only ask;
  screen_record_frame acts on the next frame. On the shutdown thread they are hidden, not
  destroyed.
- modules/Screen_Record: record_pause; while paused the source is drained but nothing is sent,
  and frames' times leave the pause out, so the video has no gap. Record_Stats.paused_seconds;
  Record_Testing.LOSE_DUPLICATION loses Desktop Duplication every 3 s.
- Recordings left in %TEMP%/mcap by a crash are offered at startup: into the library (no window
  known; size from its frames, date from its name), deleted, or later.
- [video] in settings.config: fps (1-120, 30), cursor (true), capture (auto, dxgi, wgc); written
  into existing files on the next launch.
- The Win32 GDI bindings move to src/base/platform/win32_gdi_bindings.jai, shared by the overlay
  and the toolbar.
- -record-region X Y W H and -record-ui-visible for -record; -pause SECONDS and -lose for
  record-probe. capture.json's Paused.
- Checked: the toolbar is not in a whole-screen recording's frame; a 10 s pause leaves a 6.2 s
  video of a 16 s run at a steady 30 fps (DXGI and WGC); 3 simulated losses in 10 s recover with
  no gap. Tests: a leftover's date and its write (22 checks in the recording group); all pass.
- Fixed on the way: a recording's path repeated the temporary folder ("%1" is the first argument).
2026-10-10 07:45:14 +01:00
9953ba6f1f [0.1.4] Video capture phase 2: Windows.Graphics.Capture, for when Desktop Duplication cannot run
- modules/Screen_Record/wgc_source.jai: the screen through Windows.Graphics.Capture, with
  hand-written bindings for its WinRT interfaces (capture item interop, item, free-threaded frame
  pool, frame, session 1-3, IDirect3DDxgiInterfaceAccess, IClosable). A small COM handler on
  FrameArrived sets an event per frame; when Windows refuses it the pool is polled each
  millisecond. The cursor is drawn by Windows; the yellow border is turned off where the system
  allows (Windows 11). A screen that changes size makes the pool again. WinRT's own calls are
  loaded from combase.dll at run time: runtimeobject.lib names no DLL for #library.
- capture_gpu.jai: what both sources share (the device on the screen's adapter, the region copy,
  the staging ring), so each is only "the next image's region into a texture".
- Record_Options.capture: AUTO (Desktop Duplication, else Windows.Graphics.Capture when it says
  fall_back), DXGI or WGC. Sources open on the capture thread, as WinRT objects belong to their
  apartment; record_start waits for it to say how that went. Record_Options.testing simulates the
  two-GPU failure, forces polling, or keeps the border, for record-probe.
- record-probe: -capture auto|dxgi|wgc, -simulate-unsupported, -poll, -border-test with
  -keep-border as its control, -cursor-test; DPI awareness set already is no longer reported as
  a failure.
- On the RTX 5080 laptop: auto falls back on the simulated failure; WGC records 4K at 60 fps with
  nothing dropped; its cursor and the one cursor.jai draws for duplication are the same; the
  border is off for an unpackaged app (0 yellow edge pixels, 14,214 with it kept);
  WDA_EXCLUDEFROMCAPTURE is respected; polling holds 30 fps. The plan records the results.
- README: Screen_Record's line, which held a carriage return in place of "\r" since 0.1.3.
2026-10-10 07:03:47 +01:00
06a2aacc9c [0.1.3] Video capture phase 1: modules/Screen_Record and record-probe, Desktop Duplication to NVENC
- modules/Screen_Record: a region of one screen to H.264 in a fragmented MP4. A capture thread
  duplicates the screen (DuplicateOutput1, else DuplicateOutput) on a D3D11 device of its own on
  the screen's adapter, copies the region on the GPU, reads it back through three staging
  textures and draws the cursor in (colour, masked colour and monochrome shapes). An encoder
  thread feeds FFmpeg: h264_nvenc, else h264_amf, h264_qsv, h264_mf, libx264, each tried in turn;
  BGRA straight to NVENC, swscale to NV12 or YUV 4:2:0 (BT.709) for the rest. A frame is sent
  when the screen or cursor changed and its turn on an fps schedule is due, with a repeat each
  second of an unchanged screen; the encoder falling 8 frames behind drops the oldest. A lost
  duplication (mode change, secure desktop) is made again and recording goes on. record_start
  says fall_back when duplication cannot run (unsupported, too many, rotated screen). Regions
  under 240 x 180 are refused, then sizes are rounded down to even; a process whose view of the
  desktop is scaled is refused.
- tools/record_probe.jai (jai first.jai - record_probe): lists the screens, records a screen or
  region and reports source, encoder, frames captured, sent, written and dropped, time per stage
  and file size; -legacy, -cuda, -exclude-test, -animate, -snapshot DIR, -check FILE.
- On the RTX 5080 laptop: 60 s of 4K at 30 fps, 1802 frames, none dropped, 18 MB a minute; every
  change kept at 60 fps; plain DuplicateOutput works when DPI aware; a CUDA context changes
  nothing; WDA_EXCLUDEFROMCAPTURE is respected; QSV, Media Foundation and libx264 keep up too;
  a recording killed mid-way still plays. The two-GPU failure was not reproduced: the screens are
  on the NVIDIA GPU in this mode. The plan records the results.
2026-10-10 06:48:43 +01:00
3e6e733a9d [0.0.13] Segmentation phase 4: per-folder opt-in, the segmentation scan, -segment
- .library-folders in the library (src/app/library_folders.jai): each library folder's options,
  starting with segment: true. Every folder starts off; unknown keys are kept when it is rewritten.
- The Segmentation window (Segments, above the grid): each folder's Segment checkbox, how many of
  its images are segmented, and Erase per folder; a button to segment the folders set to segment.
- The segmentation scan (src/app/segment_ui.jai) runs on the OCR thread as its own task, with the
  OCR scan's progress, pause and cancel: images only (never videos), not yet segmented, in folders
  set to segment. The engine loads with the first scan (2.4 GB of GPU memory). Each image's segs and
  mask are stored in one rewrite and its catalog entry loaded again.
- Importing into a folder set to segment segments the new images; the Import window's checkbox is
  the folder's setting.
- -segment segments and exits; -erase segments, and -erase all now includes segments.
- The catalog reads each capture's segs (class list hash, segment count). The exit overlay names the
  segmentation scan and model. segment_mcap_chunks moved into modules/Segment, shared with
  segment-probe. The quoting helpers of Capture_File's text formats are public.
- Tests: .library-folders read and written back (quoted names, unknown keys, versions).
2026-10-10 04:13:03 +01:00
8b6602119a [0.0.12] Segmentation phase 3: segs and mask chunks in .mcap files
- modules/Capture_File/segment_chunks.jai: a front "segs" chunk (text: engine, date, the image read
  and its size, the class list's hash, and each segment's id, score, area, box and class) and a back
  "mask" chunk (a segment id per pixel, run-length encoded with LEB128 lengths, longest side at most
  1024), keyed by model. No new file version: chunks came in version 3.
- mcap_store takes back chunks too, so a model's summary and mask are stored in one rewrite.
- Tests: the text round trip, the runs' edge cases (one segment over everything, none, runs across
  rows, multi-byte lengths, id 255), bad masks refused, and storing, replacing and erasing both
  chunks in a file with other chunks kept.
- segment-probe takes .mcap files (the image each is shown as; videos skipped) and with -store stores
  its segments in them. On 27 images a mask is 0.8 to 6.4 KB.
- The format README's "Segmentation chunks" section; the plan's phase 3 results.
2026-10-10 03:44:41 +01:00
cb21b73460 [0.0.11] Segmentation phases 1-2: Mask2Former exported to ONNX, and modules/Segment to run it
- segmentation-plan.html: the plan for panoptic segmentation of images, per library folder, stored
  in .mcap chunks (no file version bump), in six phases.
- tools/export_mask2former.py: exports facebook/mask2former-swin-base-coco-panoptic to ONNX (opset 17,
  dynamic batch, 384 x 384 input) and checks it against PyTorch: on 25 images, all 180 segments
  identical (IoU 1.000). Writes the class list with names for people ("sky-other-merged" is "sky"),
  and PyTorch's own segmentations for segment-probe -check.
- modules/Segment: runs the model with onnxruntime (CUDA, else the CPU) and makes segments of its
  outputs as transformers' post_process_panoptic_segmentation does: a segment id map (longest side at
  most 1024) and each segment's class, score, area and bounds. Matches PyTorch on 99.8% of pixels,
  every segment scored 0.6 or more found. 57 ms an image at batch 4 on an RTX 5080 laptop.
- tools/segment_probe.jai (jai first.jai - segment_probe): segments image files; -out draws them,
  -check compares with PyTorch, -dump-input writes the model's input.
- modules/onnxruntime: ort_run_float_outputs, for a model with several outputs.
- first.jai copies the model to bin/segment-models. The model itself is not in git
  (modules/Segment/models, 462 MB); tools/export_mask2former.py makes it.
2026-10-10 03:39:28 +01:00
baa188b0e4 [0.0.9] Import: videos and animated GIFs, in .mcap version 4
- Import brings in videos (MP4, MOV, MKV, WebM, AVI, WMV, MPEG, MTS/M2TS, FLV) on Windows, read with
  FFmpeg: the file as it was in the original, a frame a tenth of the way in (at most 3 s), turned
  the right way up, as the rendered image (what the grid, Capture window, OCR and labels see), and
  import.json with Duration, FrameRate, VideoCodec, Rotation, PosterTime, Audio, and the camera,
  date and location from the container. Elsewhere videos are skipped as "video (Windows only)".
- .mcap version 4: the original's size is a u64, so a video can be any size. Versions 1-3 still read.
  mcap_read_without_original and mcap_write_streamed read and rewrite a file with its original left
  on disk; mcap_store, mcap_erase, the import index and the import itself never hold a video in memory.
- Animated GIFs: Frames and Duration in import.json (gif_info).
- Grid: a play mark and duration on video thumbnails, GIF on animations. Capture window: what the
  video is, and Play in the default player (written out to %TEMP%\mcap).
- modules/ffmpeg: FFmpeg 6.0 bindings and the five DLLs used (avformat, avcodec, avutil, swscale,
  swresample), copied beside the executable by the build.
  swscale converts into a buffer of its own allocator with padded rows: an exact-sized one let
  its SIMD write past the end, crashing on frames of odd widths (1916, 1106 ...).
- image-probe shows videos and GIF frame counts; ocr-bench reads a video's frame.
- Tests: new video formats, display-matrix rotation, ISO 6709, video dates, GIF frames, and v4
  serialize/parse, v3 compatibility, streamed writes, store/erase and extraction.

SnagitConverter's copy of Capture_File needs syncing to read version 4 files.
2026-10-09 23:10:55 +01:00
811c096dd0 [0.0.8] Import: any folder of screenshots and photos into the library, a folder for each source
- Import window (and -import SOURCE DEST): every image under a folder (PNG, JPEG, HEIC, AVIF, WebP,
  GIF, BMP, TIFF, camera RAW) becomes an .mcap holding the file as it was, a preview turned by its
  EXIF orientation, and metadata.json and import.json Capture Text (date from EXIF, the file name or
  its time; file name as title; camera as app). .snagx files become the same .mcap SnagitConverter
  makes. Duplicates across the whole library are skipped by a hash index (.import-index); videos
  and sidecars are skipped; source files are only read.
- modules/Image_Decode: stb_image, then WIC for everything else; EXIF; orientation; previews.
  tools/image_probe.jai shows what it reads.
- modules/Snagx (and modules/jaison), copied from SnagitConverter.
- The library is a folder of folders: recursive scan, a folder filter, and a folder: search prefix.
- OCR, the viewer and ocr-bench decode any image format, turned the right way up.
- Rename D:\SnagitIndex to D:\McapIndex.
2026-10-09 08:15:12 +01:00
e2334e8be5 [0.0.0] OCR on the GPU: measured defaults for memory and batching; scan progress bars 2026-10-07 22:27:29 +01:00
ea9f055abc [0.0.0] .mcap version 2: OCR stored in each capture, search, a library scan, sorting by date; turned text 2026-10-07 21:51:18 +01:00
bb94f03b94 [0.0.0] OCR: an onnxruntime module and an OCR module, PP-OCRv5 mobile and server, on the GPU through CUDA when it can 2026-10-07 20:45:05 +01:00