Android相机预览数据旋转异常求助(Unity插件开发)
Alright, let's tackle this rotation issue head-on. The core problem here is that setDisplayOrientation() only affects how the preview is displayed on screen—it doesn't change the pixel order of the raw byte data you get in onPreviewFrame(). And params.setRotation() only applies to JPEG photos taken with the camera, not the preview stream. Plus, your original AdjustOrientation method didn't account for the fact that front and rear cameras have different sensor orientations, which is why you saw opposite rotation directions on your Pixel XL.
Here's how to fix this while sticking to the old Camera API:
Step 1: Calculate the Correct Camera Rotation Angle
First, add this method to compute the proper rotation angle based on Android's official guidelines—it handles both screen rotation and front/rear camera sensor differences:
private int getCameraDisplayOrientation(int cameraId) { Camera.CameraInfo info = new Camera.CameraInfo(); Camera.getCameraInfo(cameraId, info); int rotation = myActivity.getWindowManager().getDefaultDisplay().getRotation(); int degrees = 0; switch (rotation) { case Surface.ROTATION_0: degrees = 0; break; case Surface.ROTATION_90: degrees = 90; break; case Surface.ROTATION_180: degrees = 180; break; case Surface.ROTATION_270: degrees = 270; break; } int result; if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) { result = (info.orientation + degrees) % 360; result = (360 - result) % 360; // Compensate for front camera mirroring } else { // Rear camera result = (info.orientation - degrees + 360) % 360; } return result; }
Step 2: Update Preview Parameter Setup
Replace the orientation-handling code in your setupCamera method with this logic. We'll adjust the preview dimensions based on the rotation angle (since rotating 90/270 degrees swaps width and height):
@SuppressLint("NewApi") private void setupCamera(int cameraID, int width, int height) { Camera.Parameters params = mCamera.getParameters(); params.setRecordingHint(true); params.setPreviewFormat(17); // NV21 format (your existing choice) params.setZoom(0); params.setWhiteBalance(Camera.Parameters.WHITE_BALANCE_AUTO); params.setExposureCompensation(0); // Calculate the correct rotation angle int displayOrientation = getCameraDisplayOrientation(cameraID); mCamera.setDisplayOrientation(displayOrientation); // Swap width/height if rotation is 90 or 270 degrees int targetPreviewWidth = width; int targetPreviewHeight = height; if (displayOrientation == 90 || displayOrientation == 270) { targetPreviewWidth = height; targetPreviewHeight = width; } // Get optimal preview size for the adjusted dimensions Camera.Size previewSize = getOptimalSize(targetPreviewWidth, targetPreviewHeight, mCamera.getParameters().getSupportedPreviewSizes()); params.setPreviewSize(previewSize.width, previewSize.height); try{ mCamera.setParameters(params); } catch (Exception e){ Log.i("Unity", "ERROR: " + e.getMessage()); } Camera.Size mCameraPreviewSize = params.getPreviewSize(); prevWidth = mCameraPreviewSize.width; prevHeight = mCameraPreviewSize.height; // Log preview details (unchanged from your code) int[] fpsRange = new int[2]; params.getPreviewFpsRange(fpsRange); String previewFacts = mCameraPreviewSize.width + "x" + mCameraPreviewSize.height; if (fpsRange[0] == fpsRange[1]) { previewFacts += " @" + (fpsRange[0] / 1000.0) + "fps"; } else { previewFacts += " @[" + (fpsRange[0] / 1000.0) + " - " + (fpsRange[1] / 1000.0) + "] fps"; } Log.i("Unity", "JAVA: previewFacts=" + previewFacts); }
Step 3: Remove the Old AdjustOrientation Method
Your original AdjustOrientation method is now obsolete—delete it, and remove the call to it from your startCamera method.
Why This Works
- The
getCameraDisplayOrientationmethod correctly calculates the rotation needed for both front and rear cameras, fixing the opposite rotation issue on your Pixel XL. - By swapping width/height for 90/270 degree rotations, we ensure the preview data's dimensions match the rotated screen orientation. Unity can now render the
onPreviewFramedata directly using the updatedprevWidthandprevHeightvalues without extra rotation. setDisplayOrientationensures the preview looks correct on the Android side, while our dimension adjustment aligns the raw byte data with your Unity app's portrait layout.
Optional: Rotate Raw NV21 Data Directly
If you need the raw byte array in onPreviewFrame to be physically rotated (instead of just adjusting dimensions), you can add this NV21 rotation helper and call it in the preview callback (note: this adds performance overhead):
// Rotate NV21 data 90 degrees (add similar methods for 180/270 if needed) private byte[] rotateNV21(byte[] data, int width, int height) { byte[] rotatedData = new byte[data.length]; int ySize = width * height; int y = 0; int uvIndex = ySize; for (int x = 0; x < width; x++) { for (int yRow = height - 1; yRow >= 0; yRow--) { rotatedData[y] = data[yRow * width + x]; y++; } for (int yRow = height - 1; yRow >= 0; yRow -= 2) { rotatedData[uvIndex] = data[ySize + yRow * width + x]; rotatedData[uvIndex + 1] = data[ySize + (yRow + 1) * width + x]; uvIndex += 2; } } return rotatedData; }
Then update your preview callback:
@Override public void onPreviewFrame(byte[] data, Camera camera) { // Rotate data based on your calculated displayOrientation int displayOrientation = getCameraDisplayOrientation(mCamera.getCameraInfo().facing == Camera.CameraInfo.CAMERA_FACING_FRONT ? 1 : 0); byte[] rotatedData = rotateNV21(data, prevWidth, prevHeight); bytes = rotatedData; UnityPlayer.UnitySendMessage(gameObjectTargetName, "GetBuffer", ""); }
内容的提问来源于stack exchange,提问作者Dalanchoo

