bug_recorder 0.1.0
bug_recorder: ^0.1.0 copied to clipboard
Rolling-buffer screen recording for bug reports. Continuously keeps the last N seconds of screen activity, so a tester who shakes the phone after noticing a bug captures the steps that caused it - not [...]
0.1.0 #
Initial release.
Rolling buffer #
- Continuous hardware H.264 capture into a circular buffer of short, keyframe-aligned MP4 segments. ~6 MB holds a 20-second window.
- A trigger preserves the buffered history plus a configurable post-roll and remuxes them into one MP4 — no decode, no re-encode.
- Head trimming is exact rather than segment-rounded: output timestamps are rewritten relative to the target start, and the newest keyframe before it is re-timed to zero. This also makes a long static screen contribute one frame instead of minutes of padding.
- Retention enforces a duration window and an independent byte ceiling, and reports when the ceiling is what is truncating the buffer.
- Paused intervals are collapsed out of the timeline, so "the last 20 seconds" means 20 seconds of activity.
Platforms #
- Android (API 24+): MediaProjection + VirtualDisplay + MediaCodec into
rotating MediaMuxer segments, held by a
mediaProjectionforeground service. System-wide capture; keeps buffering in the background. - iOS (13+): ReplayKit in-app capture (
RPScreenRecorder.startCapture) into rotatingAVAssetWritersegments, merged with anAVAssetReader/AVAssetWriterpassthrough remux. App content only; foreground only. - Differences are exposed at runtime through
PlatformCapabilitiesrather than papered over.
Crash recovery #
- An append-only session manifest lets
recoverPreviousSession()salvage a recording from a run that died. Only the single unfinalised segment is lost.
Shake detection #
- Pure-Dart detector over the plugin's own accelerometer channel — no
sensors_plusdependency, and anyStream<AccelerationSample>can be substituted. - Gravity high-pass, per-axis direction-reversal counting, debounce, sliding window and cooldown. Rejects impacts, scrolling, walking and rotation.
Privacy #
pauseWhenBackgroundeddefaults totrue, because Android capture is system-wide.PrivacyShield/redactWhile—FLAG_SECUREon Android (invisible), pause on iOS (leaves a gap, and says so).- Buffers live in the cache directory, excluded from backups.
Verified on device #
- Android verified end to end on a Pixel 8 emulator (Android 17 / API 37): a requested 20 s + 10 s produced a 30.005 s recording, shake injection triggered a capture, and 8 of 32 segments were retained at steady state.
bin/probe_mp4.dart— dependency-free MP4 probe (duration, frames, keyframe spacing, bitrate) with--expect-*assertions for CI.tool/android_e2e.sh— drives the example app through the real consent flow and probes the result.- Android now enforces
frameRateexactly on API 30+ viaKEY_MAX_FPS_TO_ENCODER; without it a VirtualDisplay fed the encoder at the display refresh rate (measured 48 fps against a configured 24). - iOS is compiled and warning-free but has not been run: ReplayKit does not exist on the Simulator.
API #
BugRecorderfacade with aValueListenable<RecorderState>, a sealedBugRecorderEventstream, and a sealedBugRecorderExceptionhierarchy.- Concurrent captures are de-duplicated; platform operations are serialised.
BugRecorderScope,BugRecorderStateBuilder,PrivacyShieldwidgets.