代码之家  ›  专栏  ›  技术社区  ›  glenatron

Android Camera2为什么我的预览比例会在旋转后发生变化,然后再旋转回来?

  •  4
  • glenatron  · 技术社区  · 7 年前

    当我在我的索尼Xperia或我的测试三星S7(仿真器似乎很好)上进入相机视图时,它正确地显示了预览。当我旋转它时,预览会正确旋转,当我旋转回来时,预览会失去它的比例,视图也会扭曲。在第一次旋转之后,每次旋转回来,我都会有同样的扭曲。

    目前有效的方法是我使用SurfaceView的scaling:

        FrameLayout.LayoutParams newScale = new FrameLayout.LayoutParams(width, height, Gravity.CENTER );
        mTextureView.setLayoutParams(newScale);
        mTextureView.setScaleX( xScale);
        mTextureView.setScaleY( yScale);
    

      mCaptureSession = cameraCaptureSession;
                try {
    
                    mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE,
                            CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
    
                   mPreviewRequestBuilder.set(CaptureRequest.SCALER_CROP_REGION, cropRectangle);
                    mPreviewRequest = mPreviewRequestBuilder.build();
                    mCaptureSession.setRepeatingRequest(mPreviewRequest, getCaptureCallback(), mBackgroundHandler);
                } catch (CameraAccessException e) { 
                    // and so on.
                }
    

    ========================
    View Dimensions (720, 1184)
    Sensor Dimensions: (3984, 5512)
    Final size: 720, 1280
    ========================
    View ratio: 1.7777778 previewRatio: 1.6444445 scale: 1.0
    Image preview size is (720, 1184) scale is: (1.0) image size is (720, 1280)
    Image scale is (1.3835341, 0.925) Max Image is (3984, 5512) cropRectangle is (441, 0 -> 3543, 5512)
    

    然后我旋转它:

    ========================
    View Dimensions (1184, 720)
    Sensor Dimensions: (5512, 3984)
    Final size: 1280, 720
    ========================
    View ratio: 0.5625 previewRatio: 0.6081081 scale: 1.0
    Image preview size is (1184, 720) scale is: (1.0) view size is (1280, 720)
    Image scale is (0.925, 1.3835342) Max Image is (5512, 3984) cropRectangle is (0, 441 -> 5512, 3543)
    

    ========================
    View Dimensions (720, 1184)
    Sensor Dimensions: (3984, 5512)
    Final size: 720, 1280
    ========================
    View ratio: 1.7777778 previewRatio: 1.6444445 scale: 1.0
    Image preview size is (720, 1184) scale is: (1.0) view size is (720, 1280)
    Image scale is (1.3835341, 0.925) Max Image is (3984, 5512) cropRectangle is (441, 0 -> 3543, 5512)
    

    这些比例尺和裁剪矩形看起来和我完全一样,但由于某种原因,最后一幅图像被扭曲得更宽,就好像x比例尺接近1.8或类似。我可以在任何一个方向旋转回横向,它看起来很好,然后再转回纵向,一切又胖了。

    更新:如果我使用setXScale并在第二次运行时将其设置为1.0,而不是第一次运行时的值,那么它看起来是正确的。但是当我比较SurfaceView的变换矩阵时,当图像比例不好时,它们是相同的,所以这看起来像是对预览中不好的缩放的补偿。我可以改变缩放的行为方式,使初始预览太窄,旋转后的比例正确,但这仍然没有真正的帮助,因为我需要能够在纵向和横向之间切换,而图像不会被它扭曲,这似乎应该是默认行为。在景观中打开视图的方式与此基本相同-先打开视图时扭曲,然后再打开。

    每次都会正确地重新创建CaptureSession。

    第二次更新:调用 dumpsys SurfaceFlinger

    -------------------------------------------------------------------------------
    Layer name
               Z |  Comp Type |   Disp Frame (LTRB) |          Source Crop (LTRB)
    -------------------------------------------------------------------------------
    SurfaceView - com.myapp.CameraActivity#0   4294967294 |     Device |    0    0  720 1184 |    0.0    0.0  960.0  720.0
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    

    2 回复  |  直到 7 年前
        1
  •  0
  •   Ankit Tale    7 年前

    与FrameLayout相比,使用自定义纹理视图并通过Surface访问它将解决您的问题。

     private void createCameraPreviewSession() {
            try {
                SurfaceTexture texture = mTextureView.getSurfaceTexture();
                assert texture != null;
                texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
    
    
                Surface surface = new Surface(texture);
    
    
                mPreviewRequestBuilder
                        = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
                mPreviewRequestBuilder.addTarget(surface);
    
    
                mCameraDevice.createCaptureSession(Arrays.asList(surface, mImageReader.getSurface()),
                        new CameraCaptureSession.StateCallback() {
    
                            @Override
                            public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) {
                                // The camera is already closed
                                if (null == mCameraDevice) {
                                    return;
                                }
    
                                mCaptureSession = cameraCaptureSession;
                                try {
                                   mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE,
                                            CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
    
                                    setAutoFlash(mPreviewRequestBuilder);
                                    mPreviewRequest = mPreviewRequestBuilder.build();
                                    mCaptureSession.setRepeatingRequest(mPreviewRequest,
                                            mCaptureCallback, mBackgroundHandler);
                                } catch (CameraAccessException e) {
                                    e.printStackTrace();
                                }
                            }
    
                            @Override
                            public void onConfigureFailed(
                                    @NonNull CameraCaptureSession cameraCaptureSession) {
                                showToast("Failed");
                            }
                        }, null
                );
            } catch (CameraAccessException e) {
                e.printStackTrace();
            }
    }
    

    并根据摄像机输出从特定设备中检索旋转

     /**
         * Retrieves the JPEG orientation from the specified screen rotation.
         *
         * @param rotation The screen rotation.
         * @return The JPEG orientation (one of 0, 90, 270, and 360)
         */
        private int getOrientation(int rotation) {
            return (ORIENTATIONS.get(rotation) + mSensorOrientation + 270) % 360;
    }
    
        2
  •  0
  •   Ankit Tale    7 年前

    这就是如何设计AutoTextureView并在XML文件中调用它的方法

    public class AutoFitTextureView extends TextureView {
    
        private int mRatioWidth = 0;
        private int mRatioHeight = 0;
    
        public AutoFitTextureView(Context context) {
            this(context, null);
        }
    
        public AutoFitTextureView(Context context, AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public AutoFitTextureView(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
        }
    
        public void setAspectRatio(int width, int height) {
            if (width < 0 || height < 0) {
                throw new IllegalArgumentException("Size cannot be negative.");
            }
            mRatioWidth = width;
            mRatioHeight = height;
            requestLayout();
        }
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            int width = MeasureSpec.getSize(widthMeasureSpec);
            int height = MeasureSpec.getSize(heightMeasureSpec);
            if (0 == mRatioWidth || 0 == mRatioHeight) {
                setMeasuredDimension(width, height);
            } else {
                if (width < height * mRatioWidth / mRatioHeight) {
                    setMeasuredDimension(width, width * mRatioHeight / mRatioWidth);
                } else {
                    setMeasuredDimension(height * mRatioWidth / mRatioHeight, height);
                }
            }
        }
    
    }
    

    然后设置侦听器来跟踪你的纹理视图

    private final TextureView.SurfaceTextureListener mSurfaceTextureListener
                = new TextureView.SurfaceTextureListener() {
    
            @Override
            public void onSurfaceTextureAvailable(SurfaceTexture texture, int width, int height) {
                openCamera(width, height);
            }
    
            @Override
            public void onSurfaceTextureSizeChanged(SurfaceTexture texture, int width, int height) {
                configureTransform(width, height);
            }
    
            @Override
            public boolean onSurfaceTextureDestroyed(SurfaceTexture texture) {
                return true;
            }
    
            @Override
            public void onSurfaceTextureUpdated(SurfaceTexture texture) {
            }
    
        };