Viewing is open to everyone. Downloading and starring need a GitHub sign-in.
Sign in with GitHub
platform-integration.md
v0.1.0-build.85
Latest
Commit ede38f5
Sign in with GitHub to download. Each download is recorded with your GitHub username.
| 1 | # Integrating the CKey scanner into a camera platform |
| 2 | |
| 3 | This guide is for teams that own a camera pipeline: OS vendors, camera apps and scanning frameworks. It covers specification §6 and the benchmark plan that §12 leaves open. Adoption remains each platform's decision, and nothing here implies one has been made. |
| 4 | |
| 5 | ## What to take |
| 6 | |
| 7 | | Component | Library | Header | Notes | |
| 8 | |---|---|---|---| |
| 9 | | Codeword framing, RS, CRC | `ckey_codec` | `ckey/ckey.h` | no dependencies | |
| 10 | | Detection, refinement, decoding | `ckey_scan` | `ckey/ckey.h` | needs only `ckey_codec`; no renderer, no telemetry, no network, no file system, no threads | |
| 11 | | Payload interpretation | your code | [`spec/CKey-Payload-Conventions.md`](../spec/CKey-Payload-Conventions.md) | allowlisted schemes only; never act without a tap | |
| 12 | |
| 13 | Do not link `ckey_render`, `ckey_certify` or `ckey_telemetry` in a scanner. Configure with `-DCKEY_BUILD_TELEMETRY=OFF` to leave telemetry out of the build entirely. |
| 14 | |
| 15 | ## Pipeline for each frame |
| 16 | |
| 17 | The reference loop is `ios/CKeyReader/CameraScanner.swift`, and every SDK repeats it. |
| 18 | |
| 19 | 1. **Luma plane:** the Y plane of a 420 buffer is enough. No colour conversion is needed. |
| 20 | 2. **Find borders:** call `ckey_find_borders_gray8(gray, w, h, stride, quads, max)` with up to 6 candidates. These are oriented quadrilaterals of the continuous frame stroke. |
| 21 | 3. **Refine and decode Profile 0:** for each candidate, call `ckey_refine_quad_gray8(..., CKEY_FIELD_SCALABLE, quad)`. If it scores above 0, call `ckey_decode_scalable_gray8(...)` with an output buffer of 65,535 bytes. The decoder identifies the version and layout from the keys. |
| 22 | 4. **Legacy profiles (optional):** repeat with `CKEY_FIELD_LOCATOR`, `CKEY_FIELD_TEXT` and `CKEY_FIELD_EXTENDED` and their decoders. |
| 23 | 5. **Emit the first valid decode.** Every result has passed Reed–Solomon and CRC-32C. The candidate ranker only orders the work; it accepts nothing. |
| 24 | |
| 25 | **Threading:** every call is reentrant and keeps no global mutable state. Layout tables are built once, behind `std::call_once`. Run one frame per worker. |
| 26 | |
| 27 | **Memory:** the caller owns all buffers. The decoder allocates a few working vectors per call, proportional to the symbol's size. |
| 28 | |
| 29 | ## Security review surface |
| 30 | |
| 31 | - **Bounded inputs:** |
| 32 | - Image calls validate dimensions, strides and quads before reading. |
| 33 | - The decoders never write past `out_capacity` and report the required size instead. |
| 34 | - The `.ckey` reader, which a scanner does not need, is bounded and checks its CRC. It is fuzzed in CI with 20,000 mutations under ASan/UBSan. |
| 35 | - **No parsing of untrusted text in the scanner.** Payload bytes come back verbatim, and interpreting them is your code's job, under the conventions' safety rules. |
| 36 | - **CI hardening:** a Debug ASan/UBSan matrix runs on every push (`.github/workflows/ci.yml`). |
| 37 | - **Determinism:** fixed tables and seeded hashes. `tests/vectors/conformance-v1.json` pins codewords and geometry. |
| 38 | |
| 39 | ## Conformance |
| 40 | |
| 41 | Run `ckey-conformance --check tests/vectors/conformance-v1.json` against your build, then decode the vectors' rasters through your own image path. Any mismatch in a codeword or the geometry hash is a conformance failure ([details](../tools/conformance/README.md)). |
| 42 | |
| 43 | ## Benchmark test plan (proposed) |
| 44 | |
| 45 | This defines a latency target as specification §6 requires. Every target is stated per device, per image size, per profile/version and per percentile. Nothing has been measured on devices yet. |
| 46 | |
| 47 | | Dimension | Values | |
| 48 | |---|---| |
| 49 | | Devices | the vendor's current base model and its oldest supported model (on iOS, also the Neural Engine-free path) | |
| 50 | | Frame | 1920×1080 and 3840×2160 luma, at the camera's native rate | |
| 51 | | Symbols | P0 v1, v6, v16, v31 at level M, standard layout, plus P0.1 v6 | |
| 52 | | Printed sizes | 0.5 in, 1 in and 2 in, on matte paper and on a phone screen | |
| 53 | | Distances | as far as the camera can go with the symbol still covering 2–4 px per module | |
| 54 | | Lighting | 50, 300 and 1,000 lux; backlit; with specular glare | |
| 55 | | Motion | handheld, and on a 20°/s pan | |
| 56 | | Clutter | the symbol among QR codes, text and patterned backgrounds | |
| 57 | |
| 58 | The plan collects 300 captures per cell, labelled with the payload. From each cell it reports: |
| 59 | |
| 60 | - frame latency at p50, p90 and p99; |
| 61 | - time from first frame to first decode, at p50, p90 and p99; |
| 62 | - the rate of decoding within 1 second; |
| 63 | - false positives per 10,000 symbol-free frames; |
| 64 | - results for QR at the same printed area, for comparison. |
| 65 | |
| 66 | The proposed acceptance target is first decode at p90 under 200 ms, on the base device at 1080p, for v1–v16 at 1 in and 30 cm. This target must be confirmed with measured data before anyone quotes it. |
| 67 | |
| 68 | `ckey-bench` (in [`docs/benchmarks`](benchmarks)) runs the same metrics on synthetic frames, so a regression can be caught before device runs. |