PI3(RK3576) Android 14 USB Camera Mirror Issue

1. Problem Background

When testing USB camera for photo and video recording functionality using the system camera RK3576-SDK/packages/apps/Camera2 on the RK3576 platform with Android 14 system, the following issue was discovered:

【Issue】The USB camera preview orientation is inconsistent between photo mode and video mode, with video preview showing left-right mirroring.

2. Root Cause Analysis

In the Android Camera system, mirroring logic typically depends on the LENS_FACING metadata field:

ANDROID_LENS_FACING_FRONT / BACK / EXTERNAL

Cameras of type LENS_FACING_EXTERNAL generally do not perform mirroring by default. However, in practice, some camera apps or legacy system logic might process EXTERNAL cameras exactly like front-facing cameras, leading to incorrect preview mirroring.


3. Solutions

3.1. Solution One: Modify Camera Orientation in HAL Layer Camera HAL

In RK’s Camera HAL implementation, the default facing setting for USB cameras is ANDROID_LENS_FACING_EXTERNAL:

Location:
hardware/rockchip/camera_aidl/device/ExternalCameraDevice.cpp

Modifications:

--- a/device/ExternalCameraDevice.cpp
+++ b/device/ExternalCameraDevice.cpp
@@ -482,7 +482,8 @@ status_t ExternalCameraDevice::initDefaultCharsKeys(
     const uint8_t opticalStabilizationMode = ANDROID_LENS_OPTICAL_STABILIZATION_MODE_OFF;
     UPDATE(ANDROID_LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION, &opticalStabilizationMode, 1);
 
-    const uint8_t facing = ANDROID_LENS_FACING_EXTERNAL;
+    //const uint8_t facing = ANDROID_LENS_FACING_EXTERNAL;
+    const uint8_t facing = ANDROID_LENS_FACING_BACK;
     UPDATE(ANDROID_LENS_FACING, &facing, 1);

Notes

  • This method affects other camera type recognition logic
  • Not suitable for scenarios requiring classification evaluation based on EXTERNAL type

3.2. (Recommended) Solution Two: Keep EXTERNAL Type, Disable Mirror Logic for USB Camera in App Layer

If you want to maintain HAL standard semantics, you can handle it at the camera application layer:

Location:
packages/apps/Camera2/src/com/android/camera/TextureViewHelper.java

Modifications:

--- a/src/com/android/camera/TextureViewHelper.java
+++ b/src/com/android/camera/TextureViewHelper.java
@@ -347,7 +347,6 @@ public class TextureViewHelper implements TextureView.SurfaceTextureListener,
             Log.e(TAG, "TransformViewHelper does not support Camera API2");
         }
 
         // Only apply this fix when Current Active Module is Photo module AND
         // Phone is Nexus4 The enhancement fix b/20694189 to original fix to
         // b/19271661 ensures that the fix should only be applied when:
@@ -367,6 +366,21 @@ public class TextureViewHelper implements TextureView.SurfaceTextureListener,
             CameraDeviceInfo.Characteristics info = mCameraProvider.getCharacteristics(cameraId);
             matrix = info.getPreviewTransform(mOrientation, new RectF(0, 0, mWidth, mHeight),
                     mCaptureLayoutHelper.getPreviewRect());
+
+           int moduleIndex = mAppController.getCurrentModuleIndex();
+           boolean isRecordingModule = (moduleIndex == 1);
+           String cameraIdStr = cameraKey.toString().toLowerCase();
+           //boolean isUsbCamera = cameraIdStr.contains("usb") || cameraIdStr.contains("external");
+           boolean isUsbCamera = cameraIdStr.contains("api2='0'"); //This needs to be modified to correctly identify USB cameras, api2='0' is based on actual printout in neardi, for reference only!
+           if (isUsbCamera && isRecordingModule) {
+               Log.v(TAG, "USB camera recording: remove mirror transform");
+               matrix.postScale(-1f, 1f, mWidth / 2f, mHeight / 2f);
+           }

Advantages

  • No need to modify HAL layer, better system compatibility
  • Only affects target scenarios, precise logic

To resolve the USB camera preview and capture orientation mismatch issue on the RK3576 platform running Android 14, we applied a set of three coordinated patches across the Camera AIDL HAL and configuration files. These ensure the captured JPEG image has the same orientation as the preview display.

Complete Fix Summary:

1. Change Lens Facing from EXTERNAL to BACK

  • File: hardware/rockchip/camera_aidl/device/ExternalCameraDevice.cpp
  • Description :
    Sets the default ANDROID_LENS_FACING metadata to BACK instead of EXTERNAL, avoiding incorrect mirroring or rotation behavior by the framework.
--- a/device/ExternalCameraDevice.cpp
+++ b/device/ExternalCameraDevice.cpp
@@ -482,7 +482,8 @@ status_t ExternalCameraDevice::initDefaultCharsKeys(
     const uint8_t opticalStabilizationMode = ANDROID_LENS_OPTICAL_STABILIZATION_MODE_OFF;
     UPDATE(ANDROID_LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION, &opticalStabilizationMode, 1);
 
-    const uint8_t facing = ANDROID_LENS_FACING_EXTERNAL;
+    //const uint8_t facing = ANDROID_LENS_FACING_EXTERNAL;
+    const uint8_t facing = ANDROID_LENS_FACING_BACK;
     UPDATE(ANDROID_LENS_FACING, &facing, 1);
 
     // android.noiseReduction

2. Inject EXIF Orientation = 1 into JPEG Output

  • File: hardware/rockchip/camera_aidl/device/ExternalCameraUtils.cpp
  • Description :
    Injects a standard EXIF orientation tag (Orientation = 1, meaning 0° upright) into the JPEG output to ensure consistent image display across viewers and platforms.

Inserted snippet :

--- a/device/ExternalCameraUtils.cpp
+++ b/device/ExternalCameraUtils.cpp
@@ -701,10 +701,40 @@ int encodeJpegYU12(const Size& inSz, const YCbCrLayout& inLayout, int jpegQualit
         }
     }
 
-    /* If APP1 data was passed in, use it */
-    if (app1Buffer && app1Size) {
-        jpeg_write_marker(&cinfo, JPEG_APP0 + 1, static_cast<const JOCTET*>(app1Buffer), app1Size);
-    }
+    // EXIF Orientation tag values:
+    // Orientation = 1: Normal (0°), no rotation
+    // Orientation = 3: Rotate 180° CW (upside down)
+    // Orientation = 6: Rotate 90° CW (right turn)
+    // Orientation = 8: Rotate 270° CW (left turn or 90° CCW)
+    // Modified by dahua.xu (dahua.xu@neardi.com) on 2025-06-12
+    // Reason: Align captured JPEG orientation with preview display
+
+    uint8_t exifBuf[] = {
+        // APP1 identifier: "Exif\0\0"
+        0x45, 0x78, 0x69, 0x66, 0x00, 0x00,
+
+        // TIFF header (big endian)
+        0x4D, 0x4D,             // "MM"
+        0x00, 0x2A,             // TIFF magic number
+        0x00, 0x00, 0x00, 0x08, // Offset to first IFD
+
+        // IFD0 entry count = 1
+        0x00, 0x01,
+
+        // Tag 0x0112 (Orientation)
+        0x01, 0x12,             // Tag ID = 0x0112
+        0x00, 0x03,             // Type = SHORT
+        0x00, 0x00, 0x00, 0x01, // Count = 1
+
+        // Value = 1 (Rotate 0 CW), padded with 2 bytes
+        0x00, 0x01, 0x00, 0x00,
+
+        // Offset to next IFD = 0
+        0x00, 0x00, 0x00, 0x00
+    };
+
+    ALOGI("Inserting default EXIF Orientation tag: Rotate 0 CW (value=1)");
+    jpeg_write_marker(&cinfo, JPEG_APP0 + 1, exifBuf, sizeof(exifBuf));
 
     /* While we still have padded height left to go, keep giving it one
      * macroblock at a time. */

3. Set External Camera Orientation to 0° in Config File

  • File: device/rockchip/common/external_camera_config.xml
  • Description :
--- a/external_camera_config.xml
+++ b/external_camera_config.xml
@@ -67,6 +67,6 @@
             <!-- image size larger than the last entry will not be supported-->
         </FpsList>
         <!-- orientation -->
-        <Orientation  degree="90"/>
+        <Orientation  degree="0"/>
     </Device>
 </ExternalCamera>

** Result:**

  • Captured images are now correctly aligned with the preview (upright orientation).
  • EXIF metadata prevents incorrect rotation in gallery or third-party apps.
  • Fix avoids issues caused by incorrect lens facing or orientation configs.

The complete patch is available in
neardi_rk3576_android14_sdk_external_camera_patch.tar (20 KB)

To integrate this fix, apply all three patches to the corresponding source files and rebuild the camera provider and related modules.
Feel free to follow up in the community thread if further camera HAL issues arise.