【问题标题】:Android camera preview is always sidewaysAndroid 相机预览总是横着看
【发布时间】:2018-01-01 02:56:46
【问题描述】:

我正在编写一个 Android 插件,它将从相机预览中获取数据并将其发送到 Unity。 (由于各种原因,我无法使用 Unity WebCamTexture)我能够获取相机预览数据并将其发送到 Unity,但是图像始终旋转 90 度。我的 Unity 应用设置为始终处于纵向模式。

在我的 Pixel XL 上,前后图像以相反的方向旋转。这是我的应用在使用前置和后置摄像头时的照片。

我在 NativeCamera.java 中创建了函数 AdjustOrientation 来尝试修复方向,但调用它似乎没有任何效果。

我找到了一些地方的链接,这些地方说要添加类似于 AdjustOrientation 函数中的代码来解决问题,但它们都没有解决问题。

这是我调查过的链接,但没有解决我的问题。

Android - Camera preview is sideways

我尝试了不同的变体

mCamera.setDisplayOrientation()
params.setRotation()

但运气不好。

我只需要图像处于正确的方向

public void onPreviewFrame(byte[] data, Camera camera) 

保存的图像是否颠倒或旋转都没有关系。我只是将数据传递给我的 Unity 项目,Android 插件的唯一目的是将相机数据传递给 Unity。只要 OnPreviewFrame 中的图像数据是正确的,我就准备好了。

我知道有一个更新的 Camera2 API 并且它弃用了原来的 Camera,但我真的希望能够使用我现有的插件来解决这个问题,而不必编写使用 Camera2 的新插件。

这是我的项目的链接。

https://drive.google.com/open?id=1MD-NRVf0YhhVIiRUOiBptvSwh9wtK3V7

提前致谢。

约翰·劳里

更新。这是我的 .java 文件的来源,以便于参考。如果您查看 AdjustOrientation,您会看到我有一些代码可以尝试调整显示方向。好像没有效果。

package com.test.camerapreview;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.graphics.SurfaceTexture;
import android.hardware.Camera;
import android.opengl.GLES11Ext;
import android.opengl.GLES20;
import android.support.annotation.Dimension;
import android.util.Log;
import android.util.Size;
import android.view.Display;
import android.view.Surface;

import com.unity3d.player.UnityPlayer;
import com.google.gson.Gson;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

public class NativeCamera implements Camera.PreviewCallback {

    public static NativeCamera instance;
    public static String gameObjectTargetName;
    private static Activity myActivity;

    Camera mCamera;
    SurfaceTexture texture;
    int nativeTexturePointer = -1;
    int prevHeight;
    int prevWidth;

    //
    // Call this function first.
    //
    public static void Setup(String gameObjectName, Activity theActivity){
        gameObjectTargetName = gameObjectName;
        myActivity = theActivity;
        instance = new NativeCamera();
    }

    public int startCamera(int idx, int width, int height) {
        nativeTexturePointer = createExternalTexture();
        texture = new SurfaceTexture(nativeTexturePointer);

        mCamera = Camera.open(idx);

        setupCamera(idx, width, height);

        try {
            mCamera.setPreviewTexture(texture);
            mCamera.setPreviewCallback(this);

            AdjustOrientation(width, height);

            mCamera.startPreview();
            Log.i("Unity", "JAVA: camera started");
        } catch (IOException ioe) {
            Log.w("Unity", "JAVA: CAM LAUNCH FAILED");
        }
        return nativeTexturePointer;
    }

    public void stopCamera() {
        mCamera.setPreviewCallback(null);
        mCamera.stopPreview();
        mCamera.release();
        GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0);
        Log.i("Unity", "JAVA: Camera stopped");
    }

    private int createExternalTexture() {
        int[] textureIdContainer = new int[1];
        GLES20.glGenTextures(1, textureIdContainer, 0);
        GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textureIdContainer[0]);
        return textureIdContainer[0];
    }

    @SuppressLint("NewApi")
    private void setupCamera(int cameraID, int width, int height) {
        Camera.Parameters params = mCamera.getParameters();

        params.setRecordingHint(true);
//      params.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);
        params.setPreviewFormat(17);
        params.setZoom(0);
            // 16 ~ NV16 ~ YCbCr
            // 17 ~ NV21 ~ YCbCr ~ DEFAULT *
            // 4  ~ RGB_565
            // 256~ JPEG
            // 20 ~ YUY2 ~ YcbCr ...
            // 842094169 ~ YV12 ~ 4:2:0 YCrCb comprised of WXH Y plane, W/2xH/2 Cr & Cb. see documentation *
//      params.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
        Camera.Size previewSize = getOptimalSize(width, height, mCamera.getParameters().getSupportedPreviewSizes());
        int previewWidth = previewSize.width;
        int previewHeight = previewSize.height;
        params.setPictureSize(previewWidth, previewHeight);
        params.setPreviewSize(previewWidth, previewHeight);
        params.setWhiteBalance(Camera.Parameters.WHITE_BALANCE_AUTO);
        params.setExposureCompensation(0);

        //
        // Fix the orientation.
        //
//        int orientation = detectCameraDisplayOrientation(cameraID);
//        mCamera.setDisplayOrientation(orientation);
//        params.setRotation(orientation);

//        Camera.CameraInfo info = new Camera.CameraInfo();
//        Camera.getCameraInfo(cameraID, info);

//        Log.d("Unity", "info.orientation = " + info.orientation);

//        params.setRotation(-90);
//        mCamera.setDisplayOrientation(-90);

//        mCamera.setDisplayOrientation(90);
//        params.setRotation(90);


        try{
            mCamera.setParameters(params);
        } catch (Exception e){
            Log.i("Unity", "ERROR: " + e.getMessage());
        }

        Camera.Size mCameraPreviewSize = params.getPreviewSize();
        prevWidth = mCameraPreviewSize.width;
        prevHeight = mCameraPreviewSize.height;

        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);
    }

    private void AdjustOrientation(int width, int height) {
        Camera.Parameters parameters = mCamera.getParameters();

        Display display = myActivity.getWindowManager().getDefaultDisplay();

        if(display.getRotation() == Surface.ROTATION_0) {
            Camera.Size previewSize = getOptimalSize(height, width, mCamera.getParameters().getSupportedPreviewSizes());
            prevWidth = previewSize.width;
            prevHeight = previewSize.height;

            mCamera.setDisplayOrientation(90);
        }

        else if(display.getRotation() == Surface.ROTATION_90) {
            Camera.Size previewSize = getOptimalSize(width, height, mCamera.getParameters().getSupportedPreviewSizes());
            prevWidth = previewSize.width;
            prevHeight = previewSize.height;
        }

        else if(display.getRotation() == Surface.ROTATION_180) {
            Camera.Size previewSize = getOptimalSize(height, width, mCamera.getParameters().getSupportedPreviewSizes());
            prevWidth = previewSize.width;
            prevHeight = previewSize.height;
        }

        else { //if(display.getRotation() == Surface.ROTATION_270) {
            Camera.Size previewSize = getOptimalSize(width, height, mCamera.getParameters().getSupportedPreviewSizes());
            prevWidth = previewSize.width;
            prevHeight = previewSize.height;

            mCamera.setDisplayOrientation(180);
        }

        parameters.setPreviewSize(prevWidth, prevHeight);

        mCamera.setParameters(parameters);
    }

    private Camera.Size getOptimalSize(int width, int height, List<Camera.Size> sizes) {
        if(mCamera == null)
            return null;

        final double ASPECT_TOLERANCE = 0.1;
        double targetRatio=(double)width / height;

        if (sizes == null)
            return null;

        Camera.Size optimalSize = null;
        double minDiff = Double.MAX_VALUE;
        int targetWidth = width;

        for (Camera.Size size : sizes) {
            double ratio = (double) size.width / size.height;
            Log.i("Unity", "RES: size=" + size.width + "/" + size.height + " Aspect Ratio: " + ratio + " target width: " + width + " target height: " + height);

            if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) {
                continue;
            }

            if (Math.abs(size.width - targetWidth) < minDiff) {
                optimalSize = size;
                minDiff = Math.abs(size.width - targetWidth);
            }
        }

        if (optimalSize == null) {
            minDiff = Double.MAX_VALUE;
            for (Camera.Size size : sizes) {
                if (Math.abs(size.width - targetWidth) < minDiff) {
                    optimalSize = size;
                    minDiff = Math.abs(size.width - targetWidth);
                }
            }
        }
        Log.i("Unity", "optimal size=" + optimalSize.width + "/" + optimalSize.height + "/ Aspect Ratio: " + (double) optimalSize.width / optimalSize.height);
        return optimalSize;
    }

    public int getPreviewSizeWidth() {
        return prevWidth;
    }

    public int getPreviewSizeHeight() {
        return prevHeight;
    }

    public String GetPreviewSizes(int id) {
        Camera cam = Camera.open(id);
        Camera.Parameters params = cam.getParameters();

        Gson gson = new Gson();
        String JSON = gson.toJson(params.getSupportedPreviewSizes());

        cam.release();

        Log.d("Unity", "Supported sizes are " + JSON);

        return JSON;
    }

    public byte[] bytes;
    @Override
    public void onPreviewFrame(byte[] data, Camera camera) {
        bytes = data;

        UnityPlayer.UnitySendMessage(gameObjectTargetName, "GetBuffer", "");
    }

}

【问题讨论】:

  • 看看this
  • 在那个链接中,我看到它提到了两件事。首先是确保在清单中指定纵向模式。它是。另一件事是一些调整显示方向的代码。我的 AdjustOrientation 代码基本上已经做到了。我将编辑我的问题以包含代码。
  • 不幸的是,新旧相机 API 都以自然顺序提供预览像素。您可以手动旋转 onPreviewFrame() 接收到的帧,但这可能非常慢。最好“教”你的 Unity 组件旋转图像以进行显示。

标签: android camera orientation preview portrait


【解决方案1】:

尽管花费大量时间试图让相机使用 SetDisplayOrientation 旋转图像,但出于某种原因,这些都没有任何效果。我最终按照 Alex Cohen 的建议做了,并在 OnPreviewFrame 中手动旋转了图像

这是一个页面链接,提供了如何旋转图像的代码。

Rotate an YUV byte array on Android

【讨论】:

    【解决方案2】:

    在相机预览中...设置`mCamera.setPreviewDisplay(holder);

            mCamera = Camera.open(0);
            mCamera.setPreviewDisplay(holder);
            mCamera.startPreview();
            mCamera.setDisplayOrientation(90);`
    

    【讨论】:

    • 你说的是函数 OnPreviewFrame() 吗?那时,我们已经将数据作为参数传入,所以这似乎为时已晚。此外,没有 mCamera.setRotation() 函数。有一个 setDisplayOrientation 和一个 cameraParams.setRotation() 你指的是这个吗?
    • 在cameraPreview类中...params.setRotation(90);我的错误
    • 我现在没有坐在电脑前...只需检查 setrotation func 在哪里我猜它在 cameraPreview 类中只需将 90 作为参数传递..
    • 我已经更新了我的答案mCamera.setDisplayOrientation(90) 这样做会起作用..
    • 和哥们 mCamera.open(1) 使用前置摄像头。
    【解决方案3】:

    Camera.setDisplayOrientation 仅影响传递给 Camera.setPreviewDisplay 或 Camera.setPreviewTexture 的预览输出。这在 setDisplayOrientation 的 API reference 中有记录:

    这不会影响onPreviewFrame(byte[], Camera)、JPEG图片或录制视频传入的字节数组的顺序。

    如果你需要调整onPreviewFrame的输出,那么你需要自己做。您需要应用的旋转与您传递给 setDisplayOrientation 的值相同,顺时针方向。

    【讨论】:

      猜你喜欢
      • 2011-04-19
      • 2016-05-05
      • 2013-07-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多