Skip to content

Getting the best shot#

With LUNA ID, you can capture video stream and get the best shot on which the face is fixed in the optimal angle for further processing.

In LUNA ID for Android#

To get the best shot, call the LunaID.showCamera() method.

To receive a result, subscribe to LunaID.finishStates() for the StateFinished(val result: FinishResult) events.

A value of the result field depends on a best shot search result. Possible values are:

        class ResultSuccess(val data: FinishSuccessData) : FinishResult()

        class ResultFailed(val data: FinishFailedData) : FinishResult()

        // when camera closed before bestshot was found
        class ResultCancelled(val data: FinishCancelledData) : FinishResult()

ResultSuccess

When the best shot was found, data: FinishSuccessData will contain the found best shot and an optional path to the recorded video.

    class FinishSuccessData(
        val bestShot: BestShot,
        val videoPath: String?,
    )

ResultFailed

Search for the best shot can fail for various reasons. In case the search fails, the data: FinishFailedData type will define a reason.

    sealed class FinishFailedData {

        class InteractionFailed() : FinishFailedData()

        class LivenessCheckFailed() : FinishFailedData()

        class LivenessCheckError(val cause: Throwable?) : FinishFailedData()

        class UnknownError(val cause: Throwable?) : FinishFailedData()

    }

ResultCancelled

If a user closes camera screen before the best shot was found, data: FinishCancelledData will contain an optional path to the recorded video.

Since for getting the best shot, you open a camera in a new Activity class, pay special attention to the lifecycle of your code components. For example, the calling Activity class may be terminated or a presenter or view model may be recreated while searching for the best shot. In these cases, subscribe to any of the flows exposed via the LunaID class (.allEvents(), interactions(), and so on) with respect to a component's lifecycle. To do this, consider using the flowWithLifecycle() and launchIn() extension functions available for the Flow class in Kotlin.

Example#

The example below shows how to subscribe to the StateFinished events with respect to compontents' lifecycles:

        LunaID.finishStates()
            .flowOn(Dispatchers.IO)
            .flowWithLifecycle(lifecycleOwner.lifecycle, Lifecycle.State.STARTED)
            .onEach {
                when (it.result) {
                    is LunaID.FinishResult.ResultSuccess -> {
                        val image = (it.result as LunaID.FinishResult.ResultSuccess).data.bestShot
                    }
                    is LunaID.FinishResult.ResultCancelled -> {

                    }
                    is LunaID.FinishResult.ResultFailed -> {
                        val failReason = (it.result as LunaID.FinishResult.ResultFailed).data
                    }
                }
            }
            .launchIn(viewModelScope)

In LUNA ID for iOS#

To get the best shots, pass a value to the delegate parameter of the LMCameraBuilder.viewController camera controller instance creation function that conforms to the LMCameraDelegate protocol.

let controller = LMCameraBuilder.viewController(delegate: LMCameraDelegate,
                                                    configuration: LCLunaConfiguration,
                                                    livenessAPI: livenessAPI)

With the implementation of the LMCameraDelegate protocol, the camera controller will interact with the user application. In the implemented methods, you will receive the best shot or the corresponding error.

public protocol LMCameraDelegate: AnyObject {

    func bestShot(_ bestShot: LunaCore.LCBestShot, _ videoFile: String?)

    func error(_ error: LMCameraError, _ videoFile: String?)

}