有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

渲染到纹理的java Android相机预览被拉伸

我正在使用纹理将安卓摄像头预览发送到Unity3d,并将其拉伸。预览不会渲染到安卓中的任何视图,而是直接渲染到具有以下特性的纹理:

setPreviewTexture(texture);

然后,字节被发送到Unity3d,并在屏幕上绘制每个onPreviewFrame

相机预览大小和纹理大小设置为1024x768,Unity3d中的纹理容器大小相同。该设备的分辨率是960x540,所以它的比例不同。我正在为相机选择一个支持的分辨率,原始尺寸的比例为4:3,所以应该没有拉伸。 安卓似乎只渲染了可以渲染到屏幕上的部分的纹理——它将16:9的图像渲染为4:3的纹理。 如果我错了,请纠正我,但在这个例子中它似乎是这样工作的。 下面是一些代码:

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

    mCamera = Camera.open(idx);
    setupCamera(width, height);

    try {
        mCamera.setPreviewTexture(texture);
        mCamera.setPreviewCallback(this);
        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 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());
    Camera.Size picSize = getOptimalSize(width, height, mCamera.getParameters().getSupportedPictureSizes());

    params.setPictureSize(picSize.width, picSize.height);
    params.setPreviewSize(previewSize.width, previewSize.height);
    params.setWhiteBalance(Camera.Parameters.WHITE_BALANCE_AUTO);
    params.setExposureCompensation(0);

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

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 byte[] bytes;
@Override
public void onPreviewFrame(byte[] data, Camera camera) {
    bytes = data;

    //Log.i("Unity", "JAVA: " + Integer.toString(data.length));
    UnityPlayer.UnitySendMessage(gameObjectTargetName, "GetBuffer", "");
}

要求发送到Unity3d的纹理为1024x768,无需裁剪。有人对此有什么解决办法吗

更新(添加了C#代码)

这段代码从java获得预览:

void CreateCameraTexture() {
    _texWidth = nativeCameraObject.Call<int>("getPreviewSizeWidth");
    _texHeight = nativeCameraObject.Call<int>("getPreviewSizeHeight");
    _cameraPreview = new Texture2D(_texWidth, _texHeight, TextureFormat.Alpha8, false);
    _converter = new YUVDecode(_texWidth, _texHeight);
    shaderMat.SetFloat("_Width", _texWidth);

    if (OnCameraTextureCreated != null)
        OnCameraTextureCreated(_cameraPreview);
}

private byte[] _bytes;
public void GetBuffer(string str) {
    _bytes = nativeCameraObject.Get<byte[]>("bytes");
    SetColor(_bytes);
    _cameraPreview.LoadRawTextureData(_bytes);
    _cameraPreview.Apply();

    UpdateLibFrame();
}

void SetColor(byte[] bytes) {
    _converter.SetBytes(bytes);
    shaderMat.SetTexture("_YUVTex", _converter.yuvtexture);
}

void UpdateLibFrame() {
    if (OnFrameUpdate != null) OnFrameUpdate(_cameraPreview.GetRawTextureData());
}

另一个类中的代码显示了以下数据:

private RectTransform previewRect {
    get {
        if (!_previewRect)
            _previewRect = cameraPreview.GetComponent<RectTransform>();
        return _previewRect;
    }
}

private void GetPreviewFromCamera() {
    NativeCamera.OnCameraTextureCreated += (Texture2D cameraTex) => {
        if (cameraPreview != null && cameraPreview.texture != null)
            DestroyImmediate(cameraPreview.texture);

        previewRect.sizeDelta = new Vector2(1024, 768);

        cameraPreview.texture = cameraTex;
        cameraPreview.enabled = true;
    };
}

更新:添加到示例项目的链接

项目在这里: https://www.dropbox.com/s/wid1qa9cmq3ck6w/CameraPreview.zip?dl=0

CameraJava是一个gradle项目。输出是一个简单的过程。必须复制到资产/插件的AAR文件


共 (1) 个答案

  1. # 1 楼答案

    抱歉耽搁了。我终于有时间测试你的项目了。我觉得不错。下面是你在Unity中可以做的事情,以获得一个好的结果(尽管只针对横向进行了测试,因为你似乎还没有在android中实现旋转处理):转到播放器设置,并将默认方向设置为横向左。将画布缩放器设置为与相机图像相同的参考分辨率(1024x768)。最后将RawImage的Z旋转设置为0。由于4:3的比例,顶部和底部将被切割,但这是正常的。如果需要完整图像,请将cavas缩放器设置为匹配高度而不是宽度