Camera2 to NV21: The Silent Face-Detection Failure in ArcSoft's Android SDK
Android face-recognition integrations fail silently when Camera2 frames are converted to NV21 without aligning dimensions and orientation; the SDK's detector simply returns nothing. Matching the demo's conversion, size, and resource-release patterns is what separates a working integration from an empty result.
ArcSoft's APP Face Recognition SDK V5.0 on Android returns no detections for many developers even after they convert Camera2 frames to NV21. The cause: the conversion only repacks bytes, it does not rotate or mirror the image, and the algorithm's input dimensions must match the configured preview size. The integration path runs through two outputs from the same capture session: a TextureView for preview and an ImageReader for detection frames. The ImageReader outputs YUV_420_888, which is turned into NV21 with ArcSoftImageUtil.yuvToNv21, using each plane's row and pixel strides.
Beyond format, the guide spells out the call sequence: open the camera, create a capture session, receive onImageAvailable callbacks, convert, detect, draw, and close the image. The ImageReader's maxImages argument (5 in the demo) is a buffer count, not a frame rate. Images must be closed after synchronous processing or copied before async work. The guide also covers runtime permission checks, including re-checking after returning from settings and not treating shouldShowRequestPermissionRationale as proof of permanent denial. Lifecycle handling requires pausing and resuming the camera in onPause/onResume, and coordinating resource release to avoid double frees.
The most common reason no face is detected is that developers assume NV21 conversion handles orientation; the guide makes clear it doesn't, which explains many failed integrations.
The article's insistence on synchronous image release and copying data for async processing highlights a real memory-safety issue in Camera2's callback model.
The permission advice about re-checking after settings and not looping dialogs reflects Android's fragmented runtime permission behavior across OEMs.