Commit Graph

12 Commits

Author SHA1 Message Date
SkyLiCoder 9cb9045f87 feat: use MediaRecorder + canvas.captureStream() for MP4/WebM output
Recording now uses the browser's hardware video encoder:
- JPEG frames drawn to offscreen canvas → canvas.captureStream(30fps)
- MediaRecorder encodes to MP4 (preferred) or WebM (fallback)
- Stop triggers download of standard video file

Playback uses native <video> element — no custom parsing needed.
Massively simplified: removed packFrames/unpackFrames, no more custom
binary format.
2026-08-01 20:29:32 +08:00
SkyLiCoder d2ecb1a4ed refactor: stop recording triggers file download instead of browser storage
Recording flow: click Stop → frames packed into .mjpeg blob → browser
download triggered immediately. No more IndexedDB or OPFS.

Playback: use 'Load Recording File' button to open a .mjpeg file.
Removed all storage dependencies — pure file-based, 100% reliable.
2026-08-01 18:59:32 +08:00
SkyLiCoder 36413bbf60 fix: replace IndexedDB with Origin Private File System (OPFS)
OPFS (navigator.storage.getDirectory) provides direct filesystem
access scoped to the app's origin. Much more reliable than IndexedDB
for large blob storage — no transaction blocking, no connection issues.

Layout: recordings/{id}.frames (packed binary) + index.json (metadata)
Each recording stored as a single file write then close.
2026-08-01 18:55:06 +08:00
SkyLiCoder 97e294f1d6 fix: use singleton IndexedDB connection to prevent blocking
Replaced open/close-per-operation pattern with a single persistent
connection held for the app lifetime. This prevents the DB upgrade
(v1→v2) from being blocked by lingering connections, and eliminates
transaction contention.

Also added onblocked handler that surfaces a clear error instead of
hanging indefinitely.
2026-08-01 18:52:12 +08:00
SkyLiCoder a7697fd4a1 fix: pack all frames into single blob for IndexedDB storage
Previously each frame was stored as a separate Blob in the Recording
object, causing IndexedDB to hang when writing 100+ blobs in one
transaction. Now all frames are packed into a single binary blob:
  [4B frameCount][per frame: 4B ts, 4B len, JPEG data]

DB version bumped to 2 (old store deleted on upgrade).
PlaybackPanel unpacks frames on demand with a per-recording cache.
2026-08-01 17:03:47 +08:00
SkyLiCoder 3a5f160453 fix: add safety timeout in stopRecording, extract reset() helper
- extract reset() to ensure clean state in all exit paths
- add 5s timeout guard so UI never stays stuck on 'Saving'
- improve error messages for empty frames and save failures
2026-08-01 16:56:32 +08:00
SkyLiCoder 786230036b fix: use JPEG SOI/EOI marker scanning instead of multipart parsing
Recording now scans the fetch response for JPEG start-of-image (0xFFD8)
and end-of-image (0xFFD9) markers instead of parsing multipart boundaries.
This is more robust because:
- No dependency on boundary format quirks
- No crossOrigin attribute needed (no canvas tainting issues)
- JPEG SOI/EOI markers are reliable frame delimiters per the JPEG spec

Also added cancelRecording() cleanup at start of each new recording.
2026-08-01 16:48:44 +08:00
SkyLiCoder 88caa1e976 fix: use canvas capture for recording, remove cancel button
Recording now creates an offscreen <img> (browser-native MJPEG
decoding) and captures frames via canvas.toBlob() at 100ms intervals.
This replaces the broken fetch-based multipart parser.

- useRecorder: img+canvas capture instead of fetch+ReadableStream
- RecorderControls: removed Cancel button, only Record/Stop remain
- App.vue: removed cancel wiring
2026-08-01 16:20:48 +08:00
SkyLiCoder 386d8d5852 refactor: flatten project structure, remove ti2026h-web subdirectory 2026-08-01 16:08:44 +08:00
SkyLiCoder 6bbe83e1f1 fix: use native img tag for MJPEG live display
Live view now uses <img :src='streamUrl'> (browser-native MJPEG
support, same as the working test HTML). The fetch-based multipart
parser is kept only for recording frame capture. Also fixed
multipart boundary parsing to correctly handle boundary values
that already include leading dashes.

- LiveView receives streamUrl string, passes it directly to <img>
- useMjpegStream simplified to URL + state management
- useRecorder manages its own fetch stream independently
- RecorderControls shows recorder-specific errors
2026-08-01 16:04:20 +08:00
SkyLiCoder 97e6b4c056 feat: MJPEG stream viewer with recording and playback
- Add MJPEG stream parsing via Fetch ReadableStream (multipart/x-mixed-replace)
- Live view display with configurable stream URL (persisted to localStorage)
- Frontend recording: capture frames to memory, save to IndexedDB
- Playback panel: list saved recordings, replay with original frame timing
- Dark-themed single-page UI with Live/Playback tabs
2026-08-01 15:29:41 +08:00
SkyLiCoder 890a654729 initial commit 2026-08-01 14:54:07 +08:00