Skip to content

Releases: smileidentity/kamera-spm

kamera v1.2.1-SNAPSHOT.31

Pre-release

Choose a tag to compare

@jumaallan jumaallan released this 22 Oct 04:56

Snapshot of main at 2947cd18da6815c4312bbe4782ba83ab19a26e98, for testing only — not for production.

The current release is 1.2.0. This snapshot runs ahead of it, and the next release replaces it.

Platform This snapshot
Android com.usesmileid:kamera:1.2.1-SNAPSHOT from Maven Central's snapshot repository, https://central.sonatype.com/repository/maven-snapshots/
iOS kamera-spm exact: "1.2.1-SNAPSHOT.31"
Flutter usesmileid_kamera: 1.2.1-SNAPSHOT.31
React Native @smileid/kamera@1.2.1-SNAPSHOT.31, dist-tag snapshot

Changes since 1.2.0

Fixed

  • iOS: KameraReplay.install finds the kit's manifest: with no registry set, it read only the
    app bundle's manifest (fromBundle(.main)), so an app that bundles media but no manifest failed to
    install a fixture with manifestMissing. It now reads the device's fixtures as
    FixtureRegistry.fromDevice does: pushed, bundled, then the kit's own manifest with the app's media.

  • Replay media an app stages is read against the kit's manifest: with no manifest of its own,
    FixtureRegistry.fromDevice used the kit's and looked for the media inside the kit, which never
    carries it. It now reads the media from the app's own capture-fixtures/media: on iOS pushed into
    Documents or copied into the app bundle, on Android pushed into the files directory (media in the
    app's assets already merged with the kit's manifest). A folder counts once it holds every file the
    manifest names, so a stray or half-pushed one never hides complete media elsewhere.

  • ML Kit works in a minified app (Android): kamera-mlkit now ships a consumer rule keeping
    ML Kit's component registrars, which ML Kit finds by the class names in its manifest meta-data and
    R8 otherwise strips. Without it, FaceDetection.getClient() threw a NullPointerException in a
    minified partner build (seen on the Galaxy S25 Ultra, play-services-mlkit-face-detection 17.1.0).

  • Camera probes keep the sensor's response (iOS): a probe turns global tone mapping and
    low-light boost off for its frames where they're on, and the undo turns them back on, as Android
    takes probe frames without noise reduction or sharpening. Tone mapping fits a curve to each frame,
    which evens brightness out across an exposure probe, and low-light boost raises the gain by itself
    in the dark, which moves the noise with no probe commanding it. Kamera never turns tone mapping on,
    so this matters where a host or the system did.

  • Camera probes in the dark (iOS): a probe waits at least 15 frames of the camera's measured
    period as well as its 500 ms (twice that for an exposure bias), as Android does: a camera running
    slow in a dark room needs more time to apply a write than 500 ms gives it.

  • Camera probes and capture evidence on cameras whose capture results arrive late (Android): the
    Huawei GLA-LX1 (Android 11) delivered every capture result 40 to 100 ms after its image, past the
    10 ms Kamera waited, so no frame there carried readings: every probe failed and the evidence sink
    heard frames without exposure, ISO or focus. A probe frame is now cut as it arrives and judged when
    its result lands, and a frame the sink would hear without readings waits for its result (up to 8
    frames, then it goes without). Probes also wait at least 15 frames of the camera's own period, not
    only 500 ms: in a dark room that phone ran at 14 fps and its focus and zoom writes showed only after
    7 frames. There, five of six probes now apply (a 50 ms frame duration still can't, under 60 to
    80 ms exposures), where none did.

  • Camera probes: an exposure probe applies only once the sensor has moved and settled, not at
    the first frame that carries the new setting. Android needs the frame's exposure time × ISO to have
    moved at least half the commanded stops from a frame before the probe, then held within 0.05 stops
    from one frame to the next. iOS needs the same, past the bias's sync time, from each frame's EXIF.
    Exposure probes get twice the window (1 s), because auto-exposure takes longer to settle a stop
    than a lens or a zoom. The server's measurements found this on the first device crops: both phones'
    exposure steps had moved no brightness at all. A far-focus probe is held at the lens's hyperfocal
    distance, where a lens asked for infinity parks: the Galaxy S25 Ultra's front lens parked at 0.45
    diopters and never applied before.

Added

  • Custos adapters run the exposure swaps: kamera-custos and KameraCustos map Custos probes
    6 and 7 to ExposureLong and ExposureShort, so a schedule's swaps reach the camera.

  • Exposure swap probes (iOS): KameraProbeKind gains exposureLong and exposureShort
    (Custos probes 6 and 7, KameraCustos only). The probe sets a custom exposure: the duration
    auto-exposure chose times or over a factor, and the ISO the other way, so the frame is as bright with
    more, or fewer, photons behind it, and the undo hands exposure back to continuous auto-exposure. A new case on KameraProbeKind breaks an exhaustive switch over it;
    it is behind @_spi(KameraCustos).

  • Exposure swap probes (Android): KameraProbeKind gains ExposureLong and ExposureShort
    (Custos probes 6 and 7, kamera-custos only). The probe turns auto-exposure off and trades the
    exposure time AE chose against its ISO by a factor, so the frame is as bright with more, or fewer,
    photons behind it: the server judges the pair by its shot noise wherever the processor holds
    brightness. Kamera now reads each capture result's exposure while the evidence sink listens, so a
    swap starts from what AE chose. A new case on KameraProbeKind breaks an exhaustive
    when over it; it is restricted to kamera's own library group.

  • Custos adapters: kamera-custos (Android) and KameraCustos (iOS) connect a Kamera session
    to a Custos capture (docs/design/69, D8). KameraCustosCapture feeds the capture from Kamera's
    evidence sink, runs the schedule's camera probes, and seals each crop to the server where it's
    taken, so the SDK uploads sealed packets and never sees the pixels. attachEvidence and
    runProbe are now the adapters' alone (@RestrictTo(LIBRARY_GROUP) on Android,
    @_spi(KameraCustos) on iOS). Each applied probe names its frame by the camera time that frame
    was chained with (Custos SPEC §6.2, kind 8, field 7), which the adapter looks up from the frames
    it chained, so the server can read the exposure each crop was taken at. Until Custos publishes,
    the adapters build only with -Pcustos.repo=DIR or KAMERA_CUSTOS_PATH.

  • Camera probes (Android and iOS): KameraSession.runProbe(KameraProbe) runs one of the
    server schedule's hidden camera-liveness probes (docs/design/69, D2–D3): focus near or far, an
    exposure step, a fixed frame duration, or a zoom. While it runs, no frame reaches the collector,
    and the evidence sink still hears every one. The first frame whose own capture result shows the
    probe applied is cropped to a set of three 128×128 luma planes: the face and a background corner at
    native resolution, and a 3×3-averaged context around the face. The next frame that still shows the
    probe gives a fourth, the same background window, so the server can check the sensor's noise is
    new in every frame (65,536 bytes in all); a probe shown by one frame only is Failed. Then the probe is undone and
    analysis resumes. It answers Applied, Unsupported (a fixed-focus lens, no such rate, a
    parameter Kamera can't express, or a replay session), Failed (nothing showed it within 500 ms),
    or NotRun (another probe or a still was running, or the session wasn't), which is not evidence.
    Host controls set during a probe (exposure, zoom, night mode, a tap) apply when it ends, and a
    still waits for it. iOS has no per-frame capture results, so a lens-position or bias probe applies
    from the sync time AVFoundation's completion reports, a frame duration from the frames' own
    intervals, and a zoom on the second frame after the write; face tracking answers unsupported.
    On Android a probe's frames are taken with NOISE_REDUCTION_MODE_OFF and EDGE_MODE_OFF wherever
    the camera lists them, so the crop keeps the sensor's own noise for the server to measure; the
    undo restores the camera's processing. iOS probes read the video data output, which never merges
    frames the way the photo pipeline does.

  • Capture evidence (Android and iOS): KameraSession.attachEvidence(KameraEvidence?) hands a
    session a sink for a signed capture record (docs/design/69, D1). While one is attached, kamera
    calls onFrame with each analysed frame's camera readings: its sensor timestamp, whether that is
    on KameraEvidenceClock, exposure, ISO, AF and AE state, and the camera id. It also runs the gyro
    and calls onMotion with batches of up to 256 readings. On iOS the clock is
    mach_continuous_time, and sample-buffer and Core Motion times are moved onto it; face tracking's
    frames report ARKit's exposure. Replay reports nothing, a sink that throws is detached (Android;
    a Swift sink can't throw), and release() detaches it. Without a sink nothing changes. The types live in
    com.usesmileid.kamera.integration on the native cores only: the v12 SDKs connect them to Custos
    in their native bridges, and nothing crosses to Flutter or React Native.

  • Replay: a fixture can be installed for one use case. KameraReplay.install(fixture, useCase:)
    sets the source a session of that KameraUseCase plays, beside the default install(fixture),
    so a document job replays the card for its document step and the face for its selfie. reset()
    clears them all. Core reads them through KameraReplayHooks.sessionFrameSource(useCase:), still
    compiled out of release builds....

Read more

kamera v1.2.1-SNAPSHOT.30

Pre-release

Choose a tag to compare

@jumaallan jumaallan released this 22 Oct 04:20

Snapshot of main at 8f493edaa32907b76abadfe75e63bbd2c7ebd111, for testing only — not for production.

The current release is 1.2.0. This snapshot runs ahead of it, and the next release replaces it.

Platform This snapshot
Android com.usesmileid:kamera:1.2.1-SNAPSHOT from Maven Central's snapshot repository, https://central.sonatype.com/repository/maven-snapshots/
iOS kamera-spm exact: "1.2.1-SNAPSHOT.30"
Flutter usesmileid_kamera: 1.2.1-SNAPSHOT.30
React Native @smileid/kamera@1.2.1-SNAPSHOT.30, dist-tag snapshot

Changes since 1.2.0

Fixed

  • Replay media an app stages is read against the kit's manifest: with no manifest of its own,
    FixtureRegistry.fromDevice used the kit's and looked for the media inside the kit, which never
    carries it. It now reads the media from the app's own capture-fixtures/media: on iOS pushed into
    Documents or copied into the app bundle, on Android pushed into the files directory (media in the
    app's assets already merged with the kit's manifest). A folder counts once it holds every file the
    manifest names, so a stray or half-pushed one never hides complete media elsewhere.

  • ML Kit works in a minified app (Android): kamera-mlkit now ships a consumer rule keeping
    ML Kit's component registrars, which ML Kit finds by the class names in its manifest meta-data and
    R8 otherwise strips. Without it, FaceDetection.getClient() threw a NullPointerException in a
    minified partner build (seen on the Galaxy S25 Ultra, play-services-mlkit-face-detection 17.1.0).

  • Camera probes keep the sensor's response (iOS): a probe turns global tone mapping and
    low-light boost off for its frames where they're on, and the undo turns them back on, as Android
    takes probe frames without noise reduction or sharpening. Tone mapping fits a curve to each frame,
    which evens brightness out across an exposure probe, and low-light boost raises the gain by itself
    in the dark, which moves the noise with no probe commanding it. Kamera never turns tone mapping on,
    so this matters where a host or the system did.

  • Camera probes in the dark (iOS): a probe waits at least 15 frames of the camera's measured
    period as well as its 500 ms (twice that for an exposure bias), as Android does: a camera running
    slow in a dark room needs more time to apply a write than 500 ms gives it.

  • Camera probes and capture evidence on cameras whose capture results arrive late (Android): the
    Huawei GLA-LX1 (Android 11) delivered every capture result 40 to 100 ms after its image, past the
    10 ms Kamera waited, so no frame there carried readings: every probe failed and the evidence sink
    heard frames without exposure, ISO or focus. A probe frame is now cut as it arrives and judged when
    its result lands, and a frame the sink would hear without readings waits for its result (up to 8
    frames, then it goes without). Probes also wait at least 15 frames of the camera's own period, not
    only 500 ms: in a dark room that phone ran at 14 fps and its focus and zoom writes showed only after
    7 frames. There, five of six probes now apply (a 50 ms frame duration still can't, under 60 to
    80 ms exposures), where none did.

  • Camera probes: an exposure probe applies only once the sensor has moved and settled, not at
    the first frame that carries the new setting. Android needs the frame's exposure time × ISO to have
    moved at least half the commanded stops from a frame before the probe, then held within 0.05 stops
    from one frame to the next. iOS needs the same, past the bias's sync time, from each frame's EXIF.
    Exposure probes get twice the window (1 s), because auto-exposure takes longer to settle a stop
    than a lens or a zoom. The server's measurements found this on the first device crops: both phones'
    exposure steps had moved no brightness at all. A far-focus probe is held at the lens's hyperfocal
    distance, where a lens asked for infinity parks: the Galaxy S25 Ultra's front lens parked at 0.45
    diopters and never applied before.

Added

  • Custos adapters run the exposure swaps: kamera-custos and KameraCustos map Custos probes
    6 and 7 to ExposureLong and ExposureShort, so a schedule's swaps reach the camera.

  • Exposure swap probes (iOS): KameraProbeKind gains exposureLong and exposureShort
    (Custos probes 6 and 7, KameraCustos only). The probe sets a custom exposure: the duration
    auto-exposure chose times or over a factor, and the ISO the other way, so the frame is as bright with
    more, or fewer, photons behind it, and the undo hands exposure back to continuous auto-exposure. A new case on KameraProbeKind breaks an exhaustive switch over it;
    it is behind @_spi(KameraCustos).

  • Exposure swap probes (Android): KameraProbeKind gains ExposureLong and ExposureShort
    (Custos probes 6 and 7, kamera-custos only). The probe turns auto-exposure off and trades the
    exposure time AE chose against its ISO by a factor, so the frame is as bright with more, or fewer,
    photons behind it: the server judges the pair by its shot noise wherever the processor holds
    brightness. Kamera now reads each capture result's exposure while the evidence sink listens, so a
    swap starts from what AE chose. A new case on KameraProbeKind breaks an exhaustive
    when over it; it is restricted to kamera's own library group.

  • Custos adapters: kamera-custos (Android) and KameraCustos (iOS) connect a Kamera session
    to a Custos capture (docs/design/69, D8). KameraCustosCapture feeds the capture from Kamera's
    evidence sink, runs the schedule's camera probes, and seals each crop to the server where it's
    taken, so the SDK uploads sealed packets and never sees the pixels. attachEvidence and
    runProbe are now the adapters' alone (@RestrictTo(LIBRARY_GROUP) on Android,
    @_spi(KameraCustos) on iOS). Each applied probe names its frame by the camera time that frame
    was chained with (Custos SPEC §6.2, kind 8, field 7), which the adapter looks up from the frames
    it chained, so the server can read the exposure each crop was taken at. Until Custos publishes,
    the adapters build only with -Pcustos.repo=DIR or KAMERA_CUSTOS_PATH.

  • Camera probes (Android and iOS): KameraSession.runProbe(KameraProbe) runs one of the
    server schedule's hidden camera-liveness probes (docs/design/69, D2–D3): focus near or far, an
    exposure step, a fixed frame duration, or a zoom. While it runs, no frame reaches the collector,
    and the evidence sink still hears every one. The first frame whose own capture result shows the
    probe applied is cropped to a set of three 128×128 luma planes: the face and a background corner at
    native resolution, and a 3×3-averaged context around the face. The next frame that still shows the
    probe gives a fourth, the same background window, so the server can check the sensor's noise is
    new in every frame (65,536 bytes in all); a probe shown by one frame only is Failed. Then the probe is undone and
    analysis resumes. It answers Applied, Unsupported (a fixed-focus lens, no such rate, a
    parameter Kamera can't express, or a replay session), Failed (nothing showed it within 500 ms),
    or NotRun (another probe or a still was running, or the session wasn't), which is not evidence.
    Host controls set during a probe (exposure, zoom, night mode, a tap) apply when it ends, and a
    still waits for it. iOS has no per-frame capture results, so a lens-position or bias probe applies
    from the sync time AVFoundation's completion reports, a frame duration from the frames' own
    intervals, and a zoom on the second frame after the write; face tracking answers unsupported.
    On Android a probe's frames are taken with NOISE_REDUCTION_MODE_OFF and EDGE_MODE_OFF wherever
    the camera lists them, so the crop keeps the sensor's own noise for the server to measure; the
    undo restores the camera's processing. iOS probes read the video data output, which never merges
    frames the way the photo pipeline does.

  • Capture evidence (Android and iOS): KameraSession.attachEvidence(KameraEvidence?) hands a
    session a sink for a signed capture record (docs/design/69, D1). While one is attached, kamera
    calls onFrame with each analysed frame's camera readings: its sensor timestamp, whether that is
    on KameraEvidenceClock, exposure, ISO, AF and AE state, and the camera id. It also runs the gyro
    and calls onMotion with batches of up to 256 readings. On iOS the clock is
    mach_continuous_time, and sample-buffer and Core Motion times are moved onto it; face tracking's
    frames report ARKit's exposure. Replay reports nothing, a sink that throws is detached (Android;
    a Swift sink can't throw), and release() detaches it. Without a sink nothing changes. The types live in
    com.usesmileid.kamera.integration on the native cores only: the v12 SDKs connect them to Custos
    in their native bridges, and nothing crosses to Flutter or React Native.

  • Replay: a fixture can be installed for one use case. KameraReplay.install(fixture, useCase:)
    sets the source a session of that KameraUseCase plays, beside the default install(fixture),
    so a document job replays the card for its document step and the face for its selfie. reset()
    clears them all. Core reads them through KameraReplayHooks.sessionFrameSource(useCase:), still
    compiled out of release builds. The names and the useCase parameter are the same on Android and
    iOS.

Changed

  • Kamera's classes shrink and rename in a partner's release (Android): the consumer rules no
    longer keep kamera's packages whole. Nothing reaches kamera by name, and the public API is kept
    when the AAR is built, so a partner's R8 now treats kamera like its own code. In a minified app
    that...
Read more

kamera v1.2.1-SNAPSHOT.29

Pre-release

Choose a tag to compare

@jumaallan jumaallan released this 22 Oct 03:28

Snapshot of main at 874be0ceb49c2ca3689f1fe51c406db444df4a75, for testing only — not for production.

The current release is 1.2.0. This snapshot runs ahead of it, and the next release replaces it.

Platform This snapshot
Android com.usesmileid:kamera:1.2.1-SNAPSHOT from Maven Central's snapshot repository, https://central.sonatype.com/repository/maven-snapshots/
iOS kamera-spm exact: "1.2.1-SNAPSHOT.29"
Flutter usesmileid_kamera: 1.2.1-SNAPSHOT.29
React Native @smileid/kamera@1.2.1-SNAPSHOT.29, dist-tag snapshot

Changes since 1.2.0

Fixed

  • ML Kit works in a minified app (Android): kamera-mlkit now ships a consumer rule keeping
    ML Kit's component registrars, which ML Kit finds by the class names in its manifest meta-data and
    R8 otherwise strips. Without it, FaceDetection.getClient() threw a NullPointerException in a
    minified partner build (seen on the Galaxy S25 Ultra, play-services-mlkit-face-detection 17.1.0).

  • Camera probes keep the sensor's response (iOS): a probe turns global tone mapping and
    low-light boost off for its frames where they're on, and the undo turns them back on, as Android
    takes probe frames without noise reduction or sharpening. Tone mapping fits a curve to each frame,
    which evens brightness out across an exposure probe, and low-light boost raises the gain by itself
    in the dark, which moves the noise with no probe commanding it. Kamera never turns tone mapping on,
    so this matters where a host or the system did.

  • Camera probes in the dark (iOS): a probe waits at least 15 frames of the camera's measured
    period as well as its 500 ms (twice that for an exposure bias), as Android does: a camera running
    slow in a dark room needs more time to apply a write than 500 ms gives it.

  • Camera probes and capture evidence on cameras whose capture results arrive late (Android): the
    Huawei GLA-LX1 (Android 11) delivered every capture result 40 to 100 ms after its image, past the
    10 ms Kamera waited, so no frame there carried readings: every probe failed and the evidence sink
    heard frames without exposure, ISO or focus. A probe frame is now cut as it arrives and judged when
    its result lands, and a frame the sink would hear without readings waits for its result (up to 8
    frames, then it goes without). Probes also wait at least 15 frames of the camera's own period, not
    only 500 ms: in a dark room that phone ran at 14 fps and its focus and zoom writes showed only after
    7 frames. There, five of six probes now apply (a 50 ms frame duration still can't, under 60 to
    80 ms exposures), where none did.

  • Camera probes: an exposure probe applies only once the sensor has moved and settled, not at
    the first frame that carries the new setting. Android needs the frame's exposure time × ISO to have
    moved at least half the commanded stops from a frame before the probe, then held within 0.05 stops
    from one frame to the next. iOS needs the same, past the bias's sync time, from each frame's EXIF.
    Exposure probes get twice the window (1 s), because auto-exposure takes longer to settle a stop
    than a lens or a zoom. The server's measurements found this on the first device crops: both phones'
    exposure steps had moved no brightness at all. A far-focus probe is held at the lens's hyperfocal
    distance, where a lens asked for infinity parks: the Galaxy S25 Ultra's front lens parked at 0.45
    diopters and never applied before.

Added

  • Custos adapters run the exposure swaps: kamera-custos and KameraCustos map Custos probes
    6 and 7 to ExposureLong and ExposureShort, so a schedule's swaps reach the camera.

  • Exposure swap probes (iOS): KameraProbeKind gains exposureLong and exposureShort
    (Custos probes 6 and 7, KameraCustos only). The probe sets a custom exposure: the duration
    auto-exposure chose times or over a factor, and the ISO the other way, so the frame is as bright with
    more, or fewer, photons behind it, and the undo hands exposure back to continuous auto-exposure. A new case on KameraProbeKind breaks an exhaustive switch over it;
    it is behind @_spi(KameraCustos).

  • Exposure swap probes (Android): KameraProbeKind gains ExposureLong and ExposureShort
    (Custos probes 6 and 7, kamera-custos only). The probe turns auto-exposure off and trades the
    exposure time AE chose against its ISO by a factor, so the frame is as bright with more, or fewer,
    photons behind it: the server judges the pair by its shot noise wherever the processor holds
    brightness. Kamera now reads each capture result's exposure while the evidence sink listens, so a
    swap starts from what AE chose. A new case on KameraProbeKind breaks an exhaustive
    when over it; it is restricted to kamera's own library group.

  • Custos adapters: kamera-custos (Android) and KameraCustos (iOS) connect a Kamera session
    to a Custos capture (docs/design/69, D8). KameraCustosCapture feeds the capture from Kamera's
    evidence sink, runs the schedule's camera probes, and seals each crop to the server where it's
    taken, so the SDK uploads sealed packets and never sees the pixels. attachEvidence and
    runProbe are now the adapters' alone (@RestrictTo(LIBRARY_GROUP) on Android,
    @_spi(KameraCustos) on iOS). Each applied probe names its frame by the camera time that frame
    was chained with (Custos SPEC §6.2, kind 8, field 7), which the adapter looks up from the frames
    it chained, so the server can read the exposure each crop was taken at. Until Custos publishes,
    the adapters build only with -Pcustos.repo=DIR or KAMERA_CUSTOS_PATH.

  • Camera probes (Android and iOS): KameraSession.runProbe(KameraProbe) runs one of the
    server schedule's hidden camera-liveness probes (docs/design/69, D2–D3): focus near or far, an
    exposure step, a fixed frame duration, or a zoom. While it runs, no frame reaches the collector,
    and the evidence sink still hears every one. The first frame whose own capture result shows the
    probe applied is cropped to a set of three 128×128 luma planes: the face and a background corner at
    native resolution, and a 3×3-averaged context around the face. The next frame that still shows the
    probe gives a fourth, the same background window, so the server can check the sensor's noise is
    new in every frame (65,536 bytes in all); a probe shown by one frame only is Failed. Then the probe is undone and
    analysis resumes. It answers Applied, Unsupported (a fixed-focus lens, no such rate, a
    parameter Kamera can't express, or a replay session), Failed (nothing showed it within 500 ms),
    or NotRun (another probe or a still was running, or the session wasn't), which is not evidence.
    Host controls set during a probe (exposure, zoom, night mode, a tap) apply when it ends, and a
    still waits for it. iOS has no per-frame capture results, so a lens-position or bias probe applies
    from the sync time AVFoundation's completion reports, a frame duration from the frames' own
    intervals, and a zoom on the second frame after the write; face tracking answers unsupported.
    On Android a probe's frames are taken with NOISE_REDUCTION_MODE_OFF and EDGE_MODE_OFF wherever
    the camera lists them, so the crop keeps the sensor's own noise for the server to measure; the
    undo restores the camera's processing. iOS probes read the video data output, which never merges
    frames the way the photo pipeline does.

  • Capture evidence (Android and iOS): KameraSession.attachEvidence(KameraEvidence?) hands a
    session a sink for a signed capture record (docs/design/69, D1). While one is attached, kamera
    calls onFrame with each analysed frame's camera readings: its sensor timestamp, whether that is
    on KameraEvidenceClock, exposure, ISO, AF and AE state, and the camera id. It also runs the gyro
    and calls onMotion with batches of up to 256 readings. On iOS the clock is
    mach_continuous_time, and sample-buffer and Core Motion times are moved onto it; face tracking's
    frames report ARKit's exposure. Replay reports nothing, a sink that throws is detached (Android;
    a Swift sink can't throw), and release() detaches it. Without a sink nothing changes. The types live in
    com.usesmileid.kamera.integration on the native cores only: the v12 SDKs connect them to Custos
    in their native bridges, and nothing crosses to Flutter or React Native.

  • Replay: a fixture can be installed for one use case. KameraReplay.install(fixture, useCase:)
    sets the source a session of that KameraUseCase plays, beside the default install(fixture),
    so a document job replays the card for its document step and the face for its selfie. reset()
    clears them all. Core reads them through KameraReplayHooks.sessionFrameSource(useCase:), still
    compiled out of release builds. The names and the useCase parameter are the same on Android and
    iOS.

Changed

  • Kamera's classes shrink and rename in a partner's release (Android): the consumer rules no
    longer keep kamera's packages whole. Nothing reaches kamera by name, and the public API is kept
    when the AAR is built, so a partner's R8 now treats kamera like its own code. In a minified app
    that runs kamera, the dex is 234,656 B smaller and no kamera class keeps a readable name (167
    did). A new kamera-android/r8check app and scripts/r8-check.sh prove a minified app streams
    frames, runs ML Kit on a kamera frame and captures a still, and the new r8-check lane runs it on
    an emulator on every Android change.

  • Breaking (probe crops): a probe crop gains a fifth plane, the face window of the next frame
    that still shows the probe (Custos SPEC §6.6), so the server measures the face's noise from frame
    to frame. KameraProbeResult.CROP_BYTES / cropBytes i...

Read more

kamera v1.2.1-SNAPSHOT.28

Pre-release

Choose a tag to compare

@jumaallan jumaallan released this 21 Oct 05:51

Snapshot of main at 260467d3523abdbd9d1bab49827fb5795aec0b19, for testing only — not for production.

The current release is 1.2.0. This snapshot runs ahead of it, and the next release replaces it.

Platform This snapshot
Android com.usesmileid:kamera:1.2.1-SNAPSHOT from Maven Central's snapshot repository, https://central.sonatype.com/repository/maven-snapshots/
iOS kamera-spm exact: "1.2.1-SNAPSHOT.28"
Flutter usesmileid_kamera: 1.2.1-SNAPSHOT.28
React Native @smileid/kamera@1.2.1-SNAPSHOT.28, dist-tag snapshot

Changes since 1.2.0

Fixed

  • ML Kit works in a minified app (Android): kamera-mlkit now ships a consumer rule keeping
    ML Kit's component registrars, which ML Kit finds by the class names in its manifest meta-data and
    R8 otherwise strips. Without it, FaceDetection.getClient() threw a NullPointerException in a
    minified partner build (seen on the Galaxy S25 Ultra, play-services-mlkit-face-detection 17.1.0).

  • Camera probes keep the sensor's response (iOS): a probe turns global tone mapping and
    low-light boost off for its frames where they're on, and the undo turns them back on, as Android
    takes probe frames without noise reduction or sharpening. Tone mapping fits a curve to each frame,
    which evens brightness out across an exposure probe, and low-light boost raises the gain by itself
    in the dark, which moves the noise with no probe commanding it. Kamera never turns tone mapping on,
    so this matters where a host or the system did.

  • Camera probes in the dark (iOS): a probe waits at least 15 frames of the camera's measured
    period as well as its 500 ms (twice that for an exposure bias), as Android does: a camera running
    slow in a dark room needs more time to apply a write than 500 ms gives it.

  • Camera probes and capture evidence on cameras whose capture results arrive late (Android): the
    Huawei GLA-LX1 (Android 11) delivered every capture result 40 to 100 ms after its image, past the
    10 ms Kamera waited, so no frame there carried readings: every probe failed and the evidence sink
    heard frames without exposure, ISO or focus. A probe frame is now cut as it arrives and judged when
    its result lands, and a frame the sink would hear without readings waits for its result (up to 8
    frames, then it goes without). Probes also wait at least 15 frames of the camera's own period, not
    only 500 ms: in a dark room that phone ran at 14 fps and its focus and zoom writes showed only after
    7 frames. There, five of six probes now apply (a 50 ms frame duration still can't, under 60 to
    80 ms exposures), where none did.

  • Camera probes: an exposure probe applies only once the sensor has moved and settled, not at
    the first frame that carries the new setting. Android needs the frame's exposure time × ISO to have
    moved at least half the commanded stops from a frame before the probe, then held within 0.05 stops
    from one frame to the next. iOS needs the same, past the bias's sync time, from each frame's EXIF.
    Exposure probes get twice the window (1 s), because auto-exposure takes longer to settle a stop
    than a lens or a zoom. The server's measurements found this on the first device crops: both phones'
    exposure steps had moved no brightness at all. A far-focus probe is held at the lens's hyperfocal
    distance, where a lens asked for infinity parks: the Galaxy S25 Ultra's front lens parked at 0.45
    diopters and never applied before.

Added

  • Custos adapters run the exposure swaps: kamera-custos and KameraCustos map Custos probes
    6 and 7 to ExposureLong and ExposureShort, so a schedule's swaps reach the camera.

  • Exposure swap probes (iOS): KameraProbeKind gains exposureLong and exposureShort
    (Custos probes 6 and 7, KameraCustos only). The probe sets a custom exposure: the duration
    auto-exposure chose times or over a factor, and the ISO the other way, so the frame is as bright with
    more, or fewer, photons behind it, and the undo hands exposure back to continuous auto-exposure. A new case on KameraProbeKind breaks an exhaustive switch over it;
    it is behind @_spi(KameraCustos).

  • Exposure swap probes (Android): KameraProbeKind gains ExposureLong and ExposureShort
    (Custos probes 6 and 7, kamera-custos only). The probe turns auto-exposure off and trades the
    exposure time AE chose against its ISO by a factor, so the frame is as bright with more, or fewer,
    photons behind it: the server judges the pair by its shot noise wherever the processor holds
    brightness. Kamera now reads each capture result's exposure while the evidence sink listens, so a
    swap starts from what AE chose. A new case on KameraProbeKind breaks an exhaustive
    when over it; it is restricted to kamera's own library group.

  • Custos adapters: kamera-custos (Android) and KameraCustos (iOS) connect a Kamera session
    to a Custos capture (docs/design/69, D8). KameraCustosCapture feeds the capture from Kamera's
    evidence sink, runs the schedule's camera probes, and seals each crop to the server where it's
    taken, so the SDK uploads sealed packets and never sees the pixels. attachEvidence and
    runProbe are now the adapters' alone (@RestrictTo(LIBRARY_GROUP) on Android,
    @_spi(KameraCustos) on iOS). Each applied probe names its frame by the camera time that frame
    was chained with (Custos SPEC §6.2, kind 8, field 7), which the adapter looks up from the frames
    it chained, so the server can read the exposure each crop was taken at. Until Custos publishes,
    the adapters build only with -Pcustos.repo=DIR or KAMERA_CUSTOS_PATH.

  • Camera probes (Android and iOS): KameraSession.runProbe(KameraProbe) runs one of the
    server schedule's hidden camera-liveness probes (docs/design/69, D2–D3): focus near or far, an
    exposure step, a fixed frame duration, or a zoom. While it runs, no frame reaches the collector,
    and the evidence sink still hears every one. The first frame whose own capture result shows the
    probe applied is cropped to a set of three 128×128 luma planes: the face and a background corner at
    native resolution, and a 3×3-averaged context around the face. The next frame that still shows the
    probe gives a fourth, the same background window, so the server can check the sensor's noise is
    new in every frame (65,536 bytes in all); a probe shown by one frame only is Failed. Then the probe is undone and
    analysis resumes. It answers Applied, Unsupported (a fixed-focus lens, no such rate, a
    parameter Kamera can't express, or a replay session), Failed (nothing showed it within 500 ms),
    or NotRun (another probe or a still was running, or the session wasn't), which is not evidence.
    Host controls set during a probe (exposure, zoom, night mode, a tap) apply when it ends, and a
    still waits for it. iOS has no per-frame capture results, so a lens-position or bias probe applies
    from the sync time AVFoundation's completion reports, a frame duration from the frames' own
    intervals, and a zoom on the second frame after the write; face tracking answers unsupported.
    On Android a probe's frames are taken with NOISE_REDUCTION_MODE_OFF and EDGE_MODE_OFF wherever
    the camera lists them, so the crop keeps the sensor's own noise for the server to measure; the
    undo restores the camera's processing. iOS probes read the video data output, which never merges
    frames the way the photo pipeline does.

  • Capture evidence (Android and iOS): KameraSession.attachEvidence(KameraEvidence?) hands a
    session a sink for a signed capture record (docs/design/69, D1). While one is attached, kamera
    calls onFrame with each analysed frame's camera readings: its sensor timestamp, whether that is
    on KameraEvidenceClock, exposure, ISO, AF and AE state, and the camera id. It also runs the gyro
    and calls onMotion with batches of up to 256 readings. On iOS the clock is
    mach_continuous_time, and sample-buffer and Core Motion times are moved onto it; face tracking's
    frames report ARKit's exposure. Replay reports nothing, a sink that throws is detached (Android;
    a Swift sink can't throw), and release() detaches it. Without a sink nothing changes. The types live in
    com.usesmileid.kamera.integration on the native cores only: the v12 SDKs connect them to Custos
    in their native bridges, and nothing crosses to Flutter or React Native.

  • Replay: a fixture can be installed for one use case. KameraReplay.install(fixture, useCase:)
    sets the source a session of that KameraUseCase plays, beside the default install(fixture),
    so a document job replays the card for its document step and the face for its selfie. reset()
    clears them all. Core reads them through KameraReplayHooks.sessionFrameSource(useCase:), still
    compiled out of release builds. The names and the useCase parameter are the same on Android and
    iOS.

Changed

  • Kamera's classes shrink and rename in a partner's release (Android): the consumer rules no
    longer keep kamera's packages whole. Nothing reaches kamera by name, and the public API is kept
    when the AAR is built, so a partner's R8 now treats kamera like its own code. In a minified app
    that runs kamera, the dex is 234,656 B smaller and no kamera class keeps a readable name (167
    did). A new kamera-android/r8check app and scripts/r8-check.sh prove a minified app streams
    frames, runs ML Kit on a kamera frame and captures a still, and the new r8-check lane runs it on
    an emulator on every Android change.

  • Breaking (probe crops): a probe crop gains a fifth plane, the face window of the next frame
    that still shows the probe (Custos SPEC §6.6), so the server measures the face's noise from frame
    to frame. KameraProbeResult.CROP_BYTES / cropBytes i...

Read more

kamera v1.2.1-SNAPSHOT.27

Pre-release

Choose a tag to compare

@jumaallan jumaallan released this 18 Oct 17:40

Snapshot of main at 25dc7a4ced85c7b400ecd83ae5062fa8df2ab6ee, for testing only — not for production.

The current release is 1.2.0. This snapshot runs ahead of it, and the next release replaces it.

Platform This snapshot
Android com.usesmileid:kamera:1.2.1-SNAPSHOT from Maven Central's snapshot repository, https://central.sonatype.com/repository/maven-snapshots/
iOS kamera-spm exact: "1.2.1-SNAPSHOT.27"
Flutter usesmileid_kamera: 1.2.1-SNAPSHOT.27
React Native @smileid/kamera@1.2.1-SNAPSHOT.27, dist-tag snapshot

Changes since 1.2.0

Changed

  • Testing kit (source-breaking for raw ids): the replay face and document fixtures are new
    generated recordings, and the old ones are gone. selfie-smile-v1 becomes
    selfie-neutral-smile-v2, neutral for 2.5 s and then smiling, so iOS's smile score has the
    resting mouth it compares against. head-turns-v1 becomes head-turns-v2, a 7 s loop of right,
    left and up that brings every direction back inside the enhanced selfie's 8 s timeout.
    doc-card-v1 becomes doc-id1-desk-v1, a photoreal fictional card on a desk, sized to the SDKs'
    ID-1 outline and stored as a video like the face clips. The SelfieSmile, HeadTurns and DocCard enum cases keep their names and
    point at the new ids. Code that installs a fixture by its raw id string needs the new id.

Fixed

  • Replay: a capture screen that hands over to the next no longer stops the next screen's
    replay. Sessions share the one replay source, and the next screen's session begins before the
    outgoing one is released, so the outgoing release stopped the run it no longer owned: on iOS a
    document flow's selfie step got no frames and never completed. A session now stops the source
    only while the run it began is still the one playing. Android and iOS both.
  • iOS testing kit: replay no longer crashes the app when a session restarts mid-run, as the
    document flows do between captures. A run that begin() superseded freed its frame gate below
    its starting value, which libdispatch traps on ("Semaphore object deallocated while in use").
  • Testing kit: the selfie-smile-v1 replay fixture is padded with white instead of black.
    Its dark bars made full-length uniform rows and columns, which the server rejects as an image
    issue, so an SDK's pre-upload check retook every capture and replay never completed. The face's
    size and position are unchanged.

Added

  • All four SDKs: KameraKit.deviceProfile says how capable kamera reads the device to be
    (docs/design/61): a KameraDeviceProfile with a tier of entry, standard or high (wire
    wire.deviceTier) and the signals it came from, read once per process before any camera opens.
    Android reads the Go-tier switch, the memory, the cores the phone has, the fastest core's top
    clock and, on Android 12 and later, the chip and the media performance class; iOS reads the memory
    and the model identifier. A signal kamera can't read is null and moves the tier nowhere, and
    nothing kamera does depends on the tier yet: a host records it with each capture, so the lines can
    be set from the field. On Android it's deviceProfile(context); Flutter and React Native return a
    future and a promise that never fail. The glue's shared encoder is KameraDeviceProfileWire.
  • All four SDKs: KameraMetric.AnalysisStalled(openForMs) (wire analysisStalled) reports a
    frame the host has kept in analysis for 2 s or more (KameraDefaults.analysisStallAfterMs), once
    per frame and while it is still out (docs/design/60). Until now a host analyzer that hung froze
    analysis silently: the camera delivers no fresh frame until the frame out closes, and kamera only
    reported a frame when it closed. A frame waiting in KameraFrameHub for the host's take is not in
    analysis and never counts. Each core checks once a second on a thread of its own, only while a
    frame is out and not yet reported, and only with metrics on. Breaking for exhaustive
    matches:
    a Kotlin when, Swift switch or Dart switch over KameraMetric, or a TypeScript
    switch on metric.name that ends in a never check, needs an AnalysisStalled /
    .analysisStalled / KameraAnalysisStalledMetric / KameraMetricName.AnalysisStalled case (or a
    default).

Changed

  • Flutter and React Native: the Android glue ships ML Kit's adapter alone again by default:
    kameraMlBackend defaults to mlkit, not all (docs/design/20 D9). Since 1.2.0, HMS reached
    every app, ML Kit-only ones too: it failed a Flutter release build at R8 on Cronet classes, failed
    an Expo app's manifest merge on android:allowBackup, and turned backup off in a Flutter app.
    • To keep 1.2.0's behaviour, set -PkameraMlBackend=all (or KAMERA_ML_BACKEND=all).
    • An app that opts in settles HMS's allowBackup with tools:replace, and adds
      -dontwarn org.chromium.net.** if R8 stops on the Cronet classes (docs/guide/install.md).
    • The Smile ID SDKs' Huawei packages depend on kamera-huawei themselves, so they don't need it.
  • Flutter (Android): the preview texture is a SurfaceProducer, not a SurfaceTexture. From
    Android 10 Flutter backs it with an ImageReader, which Impeller's Vulkan backend imports as it
    is; a SurfaceTexture had to be copied into Vulkan, with the raster thread waiting on the copy,
    for every preview frame. An ImageReader shows the camera's buffers as the sensor wrote them, so
    KameraPreviewView turns them by the sensor's orientation and flips a front lens's itself. The
    glue works the turn out from CameraX's description of each camera surface and sends it as the
    camera changes, showing black in between, so a lens switch never shows the last camera's frame
    turned the new camera's way (KameraTextureTransform in the Android core, for the glue). Before
    Android 10 the texture is still a SurfaceTexture, and nothing changes. A replay's frames are
    drawn into the texture already turned back, so the same transform shows them upright.

Removed

  • Android and iOS: BestFrameTracker, the best-frame ring of frames.md M2. Nothing called it on
    either core or in the SDKs. It was internal on iOS. On Android the class itself was public,
    in the com.usesmileid.kamera.internal package but outside the API lists, so it is deleted rather
    than deprecated, as a never-used symbol (the owner's exception to deprecate-first).
    Breaking (Android, source): a host that referenced
    com.usesmileid.kamera.internal.BestFrameTracker no longer compiles. Rank recent frames over the
    public scorer, frame.qualityScore(region), which stays.

kamera v1.2.1-SNAPSHOT.26

Pre-release

Choose a tag to compare

@jumaallan jumaallan released this 18 Oct 13:05

Snapshot of main at 2ac6a0de250ef3d8f6feaa5e5e16d30d7b1fe24e, for testing only — not for production.

The current release is 1.2.0. This snapshot runs ahead of it, and the next release replaces it.

Platform This snapshot
Android com.usesmileid:kamera:1.2.1-SNAPSHOT from Maven Central's snapshot repository, https://central.sonatype.com/repository/maven-snapshots/
iOS kamera-spm exact: "1.2.1-SNAPSHOT.26"
Flutter usesmileid_kamera: 1.2.1-SNAPSHOT.26
React Native @smileid/kamera@1.2.1-SNAPSHOT.26, dist-tag snapshot

Changes since 1.2.0

Added

  • Testing kit: three synthetic portrait document replay fixtures, doc-greenbook-v1 (the
    South African Green Book's 320/428 page), doc-booklet-front-v1 and doc-booklet-back-v1 (a
    two-sided booklet at the default portrait 0.65), with ids on all four kits (GreenBook,
    BookletFront, BookletBack). Each is three 720x1280 frames drawn inside the SDKs' portrait
    document outline. Until now the only document fixture was the landscape doc-card-v1, so a
    portrait document flow had nothing to replay. scripts/gen_document_fixtures.py renders them.
    Source-breaking for an exhaustive match: these are new cases on the public Kotlin
    ReplayFixtureId and Swift ReplayFixtureID enums (greenBook, bookletFront,
    bookletBack on iOS), so a when or switch over every case needs the three new ones or an
    else/default branch to compile.

Fixed

  • Replay: a capture screen that hands over to the next no longer stops the next screen's
    replay. Sessions share the one replay source, and the next screen's session begins before the
    outgoing one is released, so the outgoing release stopped the run it no longer owned: on iOS a
    document flow's selfie step got no frames and never completed. A session now stops the source
    only while the run it began is still the one playing. Android and iOS both.
  • iOS testing kit: replay no longer crashes the app when a session restarts mid-run, as the
    document flows do between captures. A run that begin() superseded freed its frame gate below
    its starting value, which libdispatch traps on ("Semaphore object deallocated while in use").
  • Testing kit: the selfie-smile-v1 replay fixture is padded with white instead of black.
    Its dark bars made full-length uniform rows and columns, which the server rejects as an image
    issue, so an SDK's pre-upload check retook every capture and replay never completed. The face's
    size and position are unchanged.

Added

  • All four SDKs: KameraKit.deviceProfile says how capable kamera reads the device to be
    (docs/design/61): a KameraDeviceProfile with a tier of entry, standard or high (wire
    wire.deviceTier) and the signals it came from, read once per process before any camera opens.
    Android reads the Go-tier switch, the memory, the cores the phone has, the fastest core's top
    clock and, on Android 12 and later, the chip and the media performance class; iOS reads the memory
    and the model identifier. A signal kamera can't read is null and moves the tier nowhere, and
    nothing kamera does depends on the tier yet: a host records it with each capture, so the lines can
    be set from the field. On Android it's deviceProfile(context); Flutter and React Native return a
    future and a promise that never fail. The glue's shared encoder is KameraDeviceProfileWire.
  • All four SDKs: KameraMetric.AnalysisStalled(openForMs) (wire analysisStalled) reports a
    frame the host has kept in analysis for 2 s or more (KameraDefaults.analysisStallAfterMs), once
    per frame and while it is still out (docs/design/60). Until now a host analyzer that hung froze
    analysis silently: the camera delivers no fresh frame until the frame out closes, and kamera only
    reported a frame when it closed. A frame waiting in KameraFrameHub for the host's take is not in
    analysis and never counts. Each core checks once a second on a thread of its own, only while a
    frame is out and not yet reported, and only with metrics on. Breaking for exhaustive
    matches:
    a Kotlin when, Swift switch or Dart switch over KameraMetric, or a TypeScript
    switch on metric.name that ends in a never check, needs an AnalysisStalled /
    .analysisStalled / KameraAnalysisStalledMetric / KameraMetricName.AnalysisStalled case (or a
    default).

Changed

  • Flutter and React Native: the Android glue ships ML Kit's adapter alone again by default:
    kameraMlBackend defaults to mlkit, not all (docs/design/20 D9). Since 1.2.0, HMS reached
    every app, ML Kit-only ones too: it failed a Flutter release build at R8 on Cronet classes, failed
    an Expo app's manifest merge on android:allowBackup, and turned backup off in a Flutter app.
    • To keep 1.2.0's behaviour, set -PkameraMlBackend=all (or KAMERA_ML_BACKEND=all).
    • An app that opts in settles HMS's allowBackup with tools:replace, and adds
      -dontwarn org.chromium.net.** if R8 stops on the Cronet classes (docs/guide/install.md).
    • The Smile ID SDKs' Huawei packages depend on kamera-huawei themselves, so they don't need it.
  • Flutter (Android): the preview texture is a SurfaceProducer, not a SurfaceTexture. From
    Android 10 Flutter backs it with an ImageReader, which Impeller's Vulkan backend imports as it
    is; a SurfaceTexture had to be copied into Vulkan, with the raster thread waiting on the copy,
    for every preview frame. An ImageReader shows the camera's buffers as the sensor wrote them, so
    KameraPreviewView turns them by the sensor's orientation and flips a front lens's itself. The
    glue works the turn out from CameraX's description of each camera surface and sends it as the
    camera changes, showing black in between, so a lens switch never shows the last camera's frame
    turned the new camera's way (KameraTextureTransform in the Android core, for the glue). Before
    Android 10 the texture is still a SurfaceTexture, and nothing changes. A replay's frames are
    drawn into the texture already turned back, so the same transform shows them upright.

Removed

  • Android and iOS: BestFrameTracker, the best-frame ring of frames.md M2. Nothing called it on
    either core or in the SDKs. It was internal on iOS. On Android the class itself was public,
    in the com.usesmileid.kamera.internal package but outside the API lists, so it is deleted rather
    than deprecated, as a never-used symbol (the owner's exception to deprecate-first).
    Breaking (Android, source): a host that referenced
    com.usesmileid.kamera.internal.BestFrameTracker no longer compiles. Rank recent frames over the
    public scorer, frame.qualityScore(region), which stays.

kamera v1.2.1-SNAPSHOT.25

Pre-release

Choose a tag to compare

@jumaallan jumaallan released this 18 Oct 05:58

Snapshot of main at 555361812ce8a85fa8828d4cf43cc626e7661794, for testing only — not for production.

The current release is 1.2.0. This snapshot runs ahead of it, and the next release replaces it.

Platform This snapshot
Android com.usesmileid:kamera:1.2.1-SNAPSHOT from Maven Central's snapshot repository, https://central.sonatype.com/repository/maven-snapshots/
iOS kamera-spm exact: "1.2.1-SNAPSHOT.25"
Flutter usesmileid_kamera: 1.2.1-SNAPSHOT.25
React Native @smileid/kamera@1.2.1-SNAPSHOT.25, dist-tag snapshot

Changes since 1.2.0

Fixed

  • iOS testing kit: replay no longer crashes the app when a session restarts mid-run, as the
    document flows do between captures. A run that begin() superseded freed its frame gate below
    its starting value, which libdispatch traps on ("Semaphore object deallocated while in use").
  • Testing kit: the selfie-smile-v1 replay fixture is padded with white instead of black.
    Its dark bars made full-length uniform rows and columns, which the server rejects as an image
    issue, so an SDK's pre-upload check retook every capture and replay never completed. The face's
    size and position are unchanged.

Added

  • All four SDKs: KameraKit.deviceProfile says how capable kamera reads the device to be
    (docs/design/61): a KameraDeviceProfile with a tier of entry, standard or high (wire
    wire.deviceTier) and the signals it came from, read once per process before any camera opens.
    Android reads the Go-tier switch, the memory, the cores the phone has, the fastest core's top
    clock and, on Android 12 and later, the chip and the media performance class; iOS reads the memory
    and the model identifier. A signal kamera can't read is null and moves the tier nowhere, and
    nothing kamera does depends on the tier yet: a host records it with each capture, so the lines can
    be set from the field. On Android it's deviceProfile(context); Flutter and React Native return a
    future and a promise that never fail. The glue's shared encoder is KameraDeviceProfileWire.
  • All four SDKs: KameraMetric.AnalysisStalled(openForMs) (wire analysisStalled) reports a
    frame the host has kept in analysis for 2 s or more (KameraDefaults.analysisStallAfterMs), once
    per frame and while it is still out (docs/design/60). Until now a host analyzer that hung froze
    analysis silently: the camera delivers no fresh frame until the frame out closes, and kamera only
    reported a frame when it closed. A frame waiting in KameraFrameHub for the host's take is not in
    analysis and never counts. Each core checks once a second on a thread of its own, only while a
    frame is out and not yet reported, and only with metrics on. Breaking for exhaustive
    matches:
    a Kotlin when, Swift switch or Dart switch over KameraMetric, or a TypeScript
    switch on metric.name that ends in a never check, needs an AnalysisStalled /
    .analysisStalled / KameraAnalysisStalledMetric / KameraMetricName.AnalysisStalled case (or a
    default).

Changed

  • Flutter and React Native: the Android glue ships ML Kit's adapter alone again by default:
    kameraMlBackend defaults to mlkit, not all (docs/design/20 D9). Since 1.2.0, HMS reached
    every app, ML Kit-only ones too: it failed a Flutter release build at R8 on Cronet classes, failed
    an Expo app's manifest merge on android:allowBackup, and turned backup off in a Flutter app.
    • To keep 1.2.0's behaviour, set -PkameraMlBackend=all (or KAMERA_ML_BACKEND=all).
    • An app that opts in settles HMS's allowBackup with tools:replace, and adds
      -dontwarn org.chromium.net.** if R8 stops on the Cronet classes (docs/guide/install.md).
    • The Smile ID SDKs' Huawei packages depend on kamera-huawei themselves, so they don't need it.
  • Flutter (Android): the preview texture is a SurfaceProducer, not a SurfaceTexture. From
    Android 10 Flutter backs it with an ImageReader, which Impeller's Vulkan backend imports as it
    is; a SurfaceTexture had to be copied into Vulkan, with the raster thread waiting on the copy,
    for every preview frame. An ImageReader shows the camera's buffers as the sensor wrote them, so
    KameraPreviewView turns them by the sensor's orientation and flips a front lens's itself. The
    glue works the turn out from CameraX's description of each camera surface and sends it as the
    camera changes, showing black in between, so a lens switch never shows the last camera's frame
    turned the new camera's way (KameraTextureTransform in the Android core, for the glue). Before
    Android 10 the texture is still a SurfaceTexture, and nothing changes. A replay's frames are
    drawn into the texture already turned back, so the same transform shows them upright.

Removed

  • Android and iOS: BestFrameTracker, the best-frame ring of frames.md M2. Nothing called it on
    either core or in the SDKs. It was internal on iOS. On Android the class itself was public,
    in the com.usesmileid.kamera.internal package but outside the API lists, so it is deleted rather
    than deprecated, as a never-used symbol (the owner's exception to deprecate-first).
    Breaking (Android, source): a host that referenced
    com.usesmileid.kamera.internal.BestFrameTracker no longer compiles. Rank recent frames over the
    public scorer, frame.qualityScore(region), which stays.

kamera v1.2.1-SNAPSHOT.22

Pre-release

Choose a tag to compare

@jumaallan jumaallan released this 15 Oct 18:09

Snapshot of main at 41108cff36a1487bc18352ad22d96a60ab3f0b5d, for testing only — not for production.

The current release is 1.2.0. This snapshot runs ahead of it, and the next release replaces it.

Platform This snapshot
Android com.usesmileid:kamera:1.2.1-SNAPSHOT from Maven Central's snapshot repository, https://central.sonatype.com/repository/maven-snapshots/
iOS kamera-spm exact: "1.2.1-SNAPSHOT.22"
Flutter usesmileid_kamera: 1.2.1-SNAPSHOT.22
React Native @smileid/kamera@1.2.1-SNAPSHOT.22, dist-tag snapshot

Changes since 1.2.0

Added

  • All four SDKs: KameraKit.deviceProfile says how capable kamera reads the device to be
    (docs/design/61): a KameraDeviceProfile with a tier of entry, standard or high (wire
    wire.deviceTier) and the signals it came from, read once per process before any camera opens.
    Android reads the Go-tier switch, the memory, the cores the phone has, the fastest core's top
    clock and, on Android 12 and later, the chip and the media performance class; iOS reads the memory
    and the model identifier. A signal kamera can't read is null and moves the tier nowhere, and
    nothing kamera does depends on the tier yet: a host records it with each capture, so the lines can
    be set from the field. On Android it's deviceProfile(context); Flutter and React Native return a
    future and a promise that never fail. The glue's shared encoder is KameraDeviceProfileWire.
  • All four SDKs: KameraMetric.AnalysisStalled(openForMs) (wire analysisStalled) reports a
    frame the host has kept in analysis for 2 s or more (KameraDefaults.analysisStallAfterMs), once
    per frame and while it is still out (docs/design/60). Until now a host analyzer that hung froze
    analysis silently: the camera delivers no fresh frame until the frame out closes, and kamera only
    reported a frame when it closed. A frame waiting in KameraFrameHub for the host's take is not in
    analysis and never counts. Each core checks once a second on a thread of its own, only while a
    frame is out and not yet reported, and only with metrics on. Breaking for exhaustive
    matches:
    a Kotlin when, Swift switch or Dart switch over KameraMetric, or a TypeScript
    switch on metric.name that ends in a never check, needs an AnalysisStalled /
    .analysisStalled / KameraAnalysisStalledMetric / KameraMetricName.AnalysisStalled case (or a
    default).

Changed

  • Flutter and React Native: the Android glue ships ML Kit's adapter alone again by default:
    kameraMlBackend defaults to mlkit, not all (docs/design/20 D9). Since 1.2.0, HMS reached
    every app, ML Kit-only ones too: it failed a Flutter release build at R8 on Cronet classes, failed
    an Expo app's manifest merge on android:allowBackup, and turned backup off in a Flutter app.
    • To keep 1.2.0's behaviour, set -PkameraMlBackend=all (or KAMERA_ML_BACKEND=all).
    • An app that opts in settles HMS's allowBackup with tools:replace, and adds
      -dontwarn org.chromium.net.** if R8 stops on the Cronet classes (docs/guide/install.md).
    • The Smile ID SDKs' Huawei packages depend on kamera-huawei themselves, so they don't need it.
  • Flutter (Android): the preview texture is a SurfaceProducer, not a SurfaceTexture. From
    Android 10 Flutter backs it with an ImageReader, which Impeller's Vulkan backend imports as it
    is; a SurfaceTexture had to be copied into Vulkan, with the raster thread waiting on the copy,
    for every preview frame. An ImageReader shows the camera's buffers as the sensor wrote them, so
    KameraPreviewView turns them by the sensor's orientation and flips a front lens's itself. The
    glue works the turn out from CameraX's description of each camera surface and sends it as the
    camera changes, showing black in between, so a lens switch never shows the last camera's frame
    turned the new camera's way (KameraTextureTransform in the Android core, for the glue). Before
    Android 10 the texture is still a SurfaceTexture, and nothing changes. A replay's frames are
    drawn into the texture already turned back, so the same transform shows them upright.

Removed

  • Android and iOS: BestFrameTracker, the best-frame ring of frames.md M2. Nothing called it on
    either core or in the SDKs. It was internal on iOS. On Android the class itself was public,
    in the com.usesmileid.kamera.internal package but outside the API lists, so it is deleted rather
    than deprecated, as a never-used symbol (the owner's exception to deprecate-first).
    Breaking (Android, source): a host that referenced
    com.usesmileid.kamera.internal.BestFrameTracker no longer compiles. Rank recent frames over the
    public scorer, frame.qualityScore(region), which stays.

kamera v1.2.0

Choose a tag to compare

@jumaallan jumaallan released this 14 Oct 22:42

Production release v1.2.0

kamera v1.1.1-SNAPSHOT.19

Pre-release

Choose a tag to compare

@jumaallan jumaallan released this 11 Oct 22:25

Snapshot of main at 969433ed213109465635386dda33cd4ef06ffcef, for testing only — not for production.

The current release is 1.1.0. This snapshot runs ahead of it, and the next release replaces it.

Platform This snapshot
Android com.usesmileid:kamera:1.1.1-SNAPSHOT from Maven Central's snapshot repository, https://central.sonatype.com/repository/maven-snapshots/
iOS kamera-spm exact: "1.1.1-SNAPSHOT.19"
Flutter usesmileid_kamera: 1.1.1-SNAPSHOT.19
React Native @smileid/kamera@1.1.1-SNAPSHOT.19, dist-tag snapshot

Changes since 1.1.0

Added

  • All four SDKs: the face's depth map (docs/design/59). AnalysisConfig.faceDepth (off in every
    preset) asks a face-tracking session to map the face, and KameraCapabilities.faceDepth says
    whether it can: a TrueDepth iPhone. There each face's KameraFaceDepth adds flatnessMm (how far
    the surface lies off a plane: 13–15 mm for a face on an iPhone 16, about zero for a picture, flat
    or tilted), meshFitMm (how far it lies from ARKit's mesh), coverage and up to 256
    KameraFaceDepthPoints, and KameraExif writes flat, fit and cov for the server. Android
    has the same config, capability and types, and reports no depth: the capability is false and the
    flag a documented no-op. The new fields default, so existing KameraFaceDepth initialisers keep
    compiling.

    • The Lab has a Face depth tool on all four samples: on iOS it draws the depth points over the
      face with a DEPTH panel (distance, relief, flatness, mesh fit, coverage and a 3D-or-flat
      verdict); elsewhere it shows as unavailable.
  • All four SDKs: KameraKit, the front door (docs/design/58): KameraKit.version, KameraKit.prewarm
    and KameraKit.session forward to KameraSession's static prewarm and its constructor, and
    version is the coordinate the package was published as, the same string on all four (a
    snapshot reports its own, such as 1.1.1-SNAPSHOT.12). Nothing moves: KameraSession and the
    presets stay where they are. The conformance file pins the members as api.frontDoor, and
    set-version.sh, stamp-publish-version.sh and the lockstep check write and read each
    package's KameraKit.version (scripts/front-door-versions.sh).

    • It isn't Kamera, the iOS module's name: a type named after its module breaks Swift code
      that qualifies a type by module (Kamera.KameraFrame), and Xcode 26's library-evolution
      interfaces.
  • All four SDKs: setDocumentGuide(widthMm, frameWidthFraction) tells a document session's
    close-focus zoom floor (docs/design/43, D5) the document the host frames and the share of the
    displayed frame's width its guide spans. The floor then zooms just enough that the document filling
    the guide sits at the lens's closest focus distance, instead of assuming an ID-1 card across 0.9
    of the frame, which put the floor at its 2x cap on an iPhone 16 and made users hold a passport
    about twice as far away as the lens needs. A width or fraction of 0 or less, or a fraction above 1
    (a percentage such as 44.6 where 0.446 was meant), clears it; the guide outlives a reconfigure.
    The fraction is of the camera frame's width as displayed, as KameraPreviewGuide.frameWidthFraction
    holds it, not of the view's. A guide applies on the display axis it was measured on: after the
    display turns, the floor keeps the ID-1 default until the host sets the guide again. Conformance behavior.closeFocus gains guided cases.

  • All four SDKs: camera-integrity readings (docs/design/57). AnalysisConfig.integrity asks for
    them, off in every preset; a new wire field (integrity), so Flutter and React Native pass it too.
    On the two native cores each frame then carries KameraFrame.integrity (and KameraImage.integrity),
    a KameraIntegrity.

    • Its first reading, videoEffects: the system video effects active on the frame, as
      KameraVideoEffect (backgroundReplacement, centerStage, portrait, reactions,
      studioLight). iOS reads them from the bound device; face tracking's frames report nil, since
      ARKit holds the camera. Android enables no camera extension, so its frames report none.
    • KameraExif writes them as fx, sorted wire names: [] when none was active, left out when
      the frame carries no reading.
    • Why: Portrait, Studio Light, Background Replacement and Reactions edit the face and its
      background before the app sees the frame, and an app can't turn most of them off. Reporting them
      lets a host ask the user to turn one off, and lets the server weigh a frame it would otherwise
      take as the camera's own. Readings only: kamera never refuses a frame on them.
    • Which apps they reach on iOS: Portrait, Studio Light and Reactions apply only to a
      video-conferencing app (voip in UIBackgroundModes), or to an app that opts in through
      Info.plist (NSCameraPortraitEffectEnabled, NSCameraStudioLightEnabled,
      NSCameraReactionEffectsEnabled; AVCaptureDevice.h, iOS 27 SDK). A typical host never gets them,
      and its Control Center panel offers only Center Stage, which applies to every app (iPhone 17 Pro
      Max and iPhone 11, iOS 27.0).
  • Both native cores: three more KameraIntegrity readings (docs/design/57, I6, I5 and I2), on
    the frames of an AnalysisConfig.integrity session.

    • metadataAttached: whether the frame came with the camera's own metadata: the sample
      buffer's EXIF attachment on iOS, a capture result joined by timestamp on Android. A hook that
      builds new buffers drops it. Null under ARKit, whose frames always carry their exposure and
      intrinsics, so their presence would prove nothing.
    • deliveryLatencyMs: how long after its sensor timestamp the frame reached kamera. Android
      reports it only where the camera stamps frames on elapsedRealtimeNanos (REALTIME).
    • faceWidthMm: the face's physical width, its box over the focal length times its distance.
      Only face tracking has a distance today, so only iOS face tracking reports it. A face held up on
      a phone measures 40–70 mm, a real one about 140–160 mm.
    • KameraExif writes them as meta (1 or 0), lat (ms) and fw (mm).
    • Why: each is a value the frame already carries, the cheapest of design 57's detection
      readings. A replaced buffer loses the camera's metadata, a slow hook adds latency, and a phone
      held up to the camera has a face too small to be real. They are readings only, unenforced until
      the rigs (D5) show what each is worth.
  • iOS: kamera turns Center Stage off while it holds a camera that supports it
    (AVCaptureDevice.centerStageControlMode = .app, isCenterStageEnabled = false), and puts back
    the app-wide setting it found, switch and mode, on stop() and release() (docs/design/57, P2). Center Stage crops
    and pans the frame to follow the people in it, so the frames the server checks stopped being the
    lens's whole view (docs/design/42). The front cameras of recent iPads and the iPhone 17 support it.
    Control Center's Audio & Video panel still offers the switch while kamera holds it, and a tap there
    turns Center Stage on mid-session (iPhone 17 Pro Max, iOS 27.0). So kamera turns it off again on
    the next camera frame.

  • iOS: the camera's own faces on every frame (docs/design/45, D3). With
    AnalysisConfig.cameraFaces on, each camera frame carries the largest face the camera's metadata
    output reported as KameraFrame.face, source camera, in display coordinates: the box and the
    camera's faceID as its trackingId. Android has done this since design 53.

    • How a frame finds its faces: the metadata output and the video output report a frame on
      separate callbacks, in no promised order. A frame takes its own report, else the newest one from
      within 70 ms before it (a frame of lag); a frame with neither carries no face.
    • On an iPhone 17 Pro Max (iOS 27.0, front camera): every frame carried the face, with one
      trackingId throughout. The video output's transform reports a box in the active format's pixels
      (1920×1440 for a 640×480 buffer), not 0..1, so kamera divides by the format's size. Before that,
      every box fell off the buffer, and no frame carried a face.
    • Not yet: the camera's roll and yaw. Their convention against kamera's (the person's own
      point of view) waits for a device run, so they stay nil.
    • Why: the analyzers see a frame about every 400 ms on the floor phone, and the camera reports
      faces on every frame it streams, at no ML cost: face-priority exposure at the camera's rate, and
      design 57's I1 (the camera's faces against the analyzer's).
  • All four SDKs: KameraPreviewGuide (docs/design/56, D1) works out where to lay a preview out so
    a host's guide (a selfie oval, a card's outline) shows the same part of the frame on every screen.
    previewRect has the frame's own aspect, scaled so frameWidthFraction of the frame's width spans
    the guide's width and centred on it. regionInFrame maps a container rectangle into the frame, and
    rectInContainer maps a frame region back. It only computes layout: no preview changes.

    • Why: a host's fill limits are a share of the frame, but its guide was a share of the
      screen. A face at 13% of a 3:4 frame spanned 84% of ios-v12's oval on a 6.1" iPhone, 69% on an
      iPhone SE and 56% on an 11" iPad.
    • With the guide: 13% spans 70% of the oval on all three.
    • Conformance: checked against behavior.previewGuide on all four SDKs.
  • All four SDKs: device motion (docs/design/55). AnalysisConfig.deviceMotion asks for the
    device's motion on every frame, off in every preset. On both native cores KameraFrame.motion (and
    KameraImage.motion) carries the frame's e...

Read more