相机预览人像设置参数失败

Posted

技术标签:

【中文标题】相机预览人像设置参数失败【英文标题】:camera preview portrait setParameters failed 【发布时间】:2013-06-18 11:45:51 【问题描述】:

我想获得全屏肖像相机预览。使用 Google 的示例,预览在纵向屏幕上以横向格式显示。当我将预览设置为 480 x 864(工作横向尺寸的倒数)时,我收到 setParameters failed 错误。

我已经阅读了我能找到的所有 ***,但我不明白为什么横向支持的尺寸在纵向上会失败。我也找不到强制全屏纵向预览的方法。手机上的相机应用程序提供全屏肖像预览,所以我知道这不是硬件限制。

我正在使用运行 android 2.3.3 的 Droid 3 进行测试

如果有任何建议,我将不胜感激。

public class SimplePreview extends ViewGroup implements SurfaceHolder.Callback 
private final String TAG = "Preview";

SurfaceView mSurfaceView;
SurfaceHolder mHolder;
Size mPreviewSize;
List<Size> mSupportedPreviewSizes;
Camera mCamera;
int cameraId;
Activity activity;

public SimplePreview(Activity context, int defaultCamera) 
    super(context);
    activity = context;
    cameraId = defaultCamera;
    mSurfaceView = new SurfaceView(context);
    addView(mSurfaceView);

    // Install a SurfaceHolder.Callback so we get notified when the
    // underlying surface is created and destroyed.
    mHolder = mSurfaceView.getHolder();
    mHolder.addCallback(this);
    mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);


public void setCamera(Camera camera) 
    mCamera = camera;
    if (mCamera != null) 
        mSupportedPreviewSizes = mCamera.getParameters()
                .getSupportedPreviewSizes();
        requestLayout();
    


public void switchCamera(Camera camera) 
    setCamera(camera);
    try 
        camera.setPreviewDisplay(mHolder);
     catch (IOException exception) 
        Log.e(TAG, "IOException caused by setPreviewDisplay()", exception);
    
    Camera.Parameters parameters = camera.getParameters();
    parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
    requestLayout();

    camera.setParameters(parameters);


@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) 
    // We purposely disregard child measurements because act as a
    // wrapper to a SurfaceView that centers the camera preview instead
    // of stretching it.
    final int width = resolveSize(getSuggestedMinimumWidth(),
            widthMeasureSpec);
    final int height = resolveSize(getSuggestedMinimumHeight(),
            heightMeasureSpec);

    Log.i(TAG, "setting View measured dimensions to width: " + width
            + "  height: " + height);

    setMeasuredDimension(width, height);

    if (mSupportedPreviewSizes != null) 
        mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, width,
                height);
    


@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) 

    if (changed && getChildCount() > 0) 
        final View child = getChildAt(0);

        final int width = r - l;
        final int height = b - t;

        int previewWidth = width;
        int previewHeight = height;
        if (mPreviewSize != null) 
            previewWidth = mPreviewSize.width;
            previewHeight = mPreviewSize.height;
        

        // Center the child SurfaceView within the parent.
        if (width * previewHeight > height * previewWidth) 
            final int scaledChildWidth = previewWidth * height
                    / previewHeight;
            child.layout((width - scaledChildWidth) / 2, 0,
                    (width + scaledChildWidth) / 2, height);
         else 
            final int scaledChildHeight = previewHeight * width
                    / previewWidth;
            child.layout(0, (height - scaledChildHeight) / 2, width,
                    (height + scaledChildHeight) / 2);
        
    


public void surfaceDestroyed(SurfaceHolder holder) 
    // Surface will be destroyed when we return, so stop the preview.
    if (mCamera != null) 
        mCamera.stopPreview();
    


private Size getOptimalPreviewSize(List<Size> sizes, int w, int h) 
    final double ASPECT_TOLERANCE = 0.1;
    double targetRatio = (double) w / h;
    if (sizes == null)
        return null;

    Size optimalSize = null;
    double minDiff = Double.MAX_VALUE;

    int targetHeight = h;

    // Try to find an size match aspect ratio and size
    for (Size size : sizes) 
        Log.v(TAG, "  width: " + size.width + "  height: " + size.height);

        double ratio = (double) size.width / size.height;
        if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE)
            continue;
        if (Math.abs(size.height - targetHeight) < minDiff) 
            optimalSize = size;
            minDiff = Math.abs(size.height - targetHeight);
        
    

    // Cannot find the one match the aspect ratio, ignore the requirement
    if (optimalSize == null) 
        minDiff = Double.MAX_VALUE;
        for (Size size : sizes) 
            if (Math.abs(size.height - targetHeight) < minDiff) 
                optimalSize = size;
                minDiff = Math.abs(size.height - targetHeight);
            
        
    

    Log.i(TAG, "optimal preview width: " + optimalSize.width + "  height: "
            + optimalSize.height);

    return optimalSize;


public void surfaceCreated(SurfaceHolder holder) 
    // The Surface has been created, acquire the camera and tell it where
    // to draw.
    try 
        if (mCamera != null) 

            final int width = getWidth();
            final int height = getHeight();

            Log.i(TAG, "view width: " + width + "  height: " + height);

            if (height > width) 
                Log.i(TAG, "in portrait mode so rotate camera preview");
                // THis line fixed the camera display orientation. seems to
                // have to be called before setPreviewDisplay()

                mCamera.setDisplayOrientation(90);

            

            mCamera.setPreviewDisplay(holder);

        
     catch (IOException exception) 
        Log.e(TAG, "IOException caused by setPreviewDisplay()", exception);
    


/**
 * orientation and rotation work done here. Now that the size is known, set
 * up the camera parameters and begin the preview.
 */
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) 

    if (mCamera != null) 

        mCamera.stopPreview();

        Camera.Parameters parameters = mCamera.getParameters();

        // mCamera.setDisplayOrientation(90);
        final int width = getWidth();
        final int height = getHeight();

        Log.i(TAG, "view width: " + width + "  height: " + height);

        if (height > width) 

            Log.i(TAG, "portrait:  setting preview width: " + 480
                    + "  height: " + 864);

            parameters.setPreviewSize(480, 864);

            // had no effect
            parameters.set("orientation", "portrait");

         else 
            Log.i(TAG, "landscape:  setting preview width: "
                    + mPreviewSize.width + "  height: "
                    + mPreviewSize.height);

            parameters.setPreviewSize(mPreviewSize.width,
                    mPreviewSize.height);
        
        requestLayout();

                    // *** following line throws setParameters failed error ***
        mCamera.setParameters(parameters);

        mCamera.startPreview();
    


以及来自 Activity 的 onCreate() 的调用:

preview = new SimplePreview(this, defaultCameraId);
    setContentView(preview);

【问题讨论】:

【参考方案1】:

试试这个代码:

package com.example.dragme;

import java.io.IOException;
import java.util.List;
import android.app.Activity;
import android.content.Context;
import android.content.res.Configuration;
import android.hardware.Camera;
import android.os.Build;
import android.util.Log;
import android.view.Display;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback 
    private SurfaceHolder mHolder;
    public Camera mCamera;

    private static boolean DEBUGGING = true;
    private static final String LOG_TAG = "CameraPreviewSample";
    private static final String CAMERA_PARAM_ORIENTATION = "orientation";
    private static final String CAMERA_PARAM_LANDSCAPE = "landscape";
    private static final String CAMERA_PARAM_PORTRAIT = "portrait";
    protected Activity mActivity;

    protected List<Camera.Size> mPreviewSizeList;
    protected List<Camera.Size> mPictureSizeList;
    protected Camera.Size mPreviewSize;
    protected Camera.Size mPictureSize;


    public CameraPreview(Context context, Camera camera) 
        super(context);

        mActivity=(Activity)context;
        mCamera = camera;

        // Install a SurfaceHolder.Callback so we get notified when the
        // underlying surface is created and destroyed.
        mHolder = getHolder();
        mHolder.addCallback(this);
        // deprecated setting, but required on Android versions prior to 3.0
        mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);


    

    public void surfaceCreated(SurfaceHolder holder) 
        // The Surface has been created, now tell the camera where to draw the preview.
        try 
            mCamera.setPreviewDisplay(holder);
            mCamera.startPreview();
         catch (IOException e) 
            Log.d("CameraView", "Error setting camera preview: " + e.getMessage());
        
    

    public void surfaceDestroyed(SurfaceHolder holder) 
        // empty. Take care of releasing the Camera preview in your activity.
    

    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) 
        // If your preview can change or rotate, take care of those events here.
        // Make sure to stop the preview before resizing or reformatting it.

        if (mHolder.getSurface() == null)
          // preview surface does not exist
          return;
        

        // stop preview before making changes
        try 
           // mCamera.stopPreview();
         catch (Exception e)
          // ignore: tried to stop a non-existent preview
        

        // set preview size and make any resize, rotate or
        // reformatting changes here

        // start preview with new settings
        try 
            Camera.Parameters cameraParams = mCamera.getParameters();
            boolean portrait = isPortrait();
            configureCameraParameters(cameraParams, portrait);

            mCamera.setPreviewDisplay(mHolder);
            mCamera.startPreview();

         catch (Exception e)
            Log.d("CameraView", "Error starting camera preview: " + e.getMessage());
        
    

    public void onPause() 
        if (null == mCamera) 
            return;
        
        mCamera.stopPreview();
        mCamera.release();
        mCamera = null;
    


    protected void configureCameraParameters(Camera.Parameters cameraParams, boolean portrait) 
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.FROYO)  // for 2.1 and before
            if (portrait) 
                cameraParams.set(CAMERA_PARAM_ORIENTATION, CAMERA_PARAM_PORTRAIT);
             else 
                cameraParams.set(CAMERA_PARAM_ORIENTATION, CAMERA_PARAM_LANDSCAPE);
            
         else  // for 2.2 and later
            int angle;
            Display display = mActivity.getWindowManager().getDefaultDisplay();
            switch (display.getRotation()) 
                case Surface.ROTATION_0: // This is display orientation
                    angle = 90; // This is camera orientation
                    break;
                case Surface.ROTATION_90:
                    angle = 0;
                    break;
                case Surface.ROTATION_180:
                    angle = 270;
                    break;
                case Surface.ROTATION_270:
                    angle = 180;
                    break;
                default:
                    angle = 90;
                    break;
            
            Log.v(LOG_TAG, "angle: " + angle);
            mCamera.setDisplayOrientation(angle);
        

        cameraParams.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
        cameraParams.setPictureSize(mPictureSize.width, mPictureSize.height);
        if (DEBUGGING) 
            Log.v(LOG_TAG, "Preview Actual Size - w: " + mPreviewSize.width + ", h: " + mPreviewSize.height);
            Log.v(LOG_TAG, "Picture Actual Size - w: " + mPictureSize.width + ", h: " + mPictureSize.height);
        

        mCamera.setParameters(cameraParams);
    


    public boolean isPortrait() 
        return (mActivity.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT);
    

【讨论】:

谢谢。此预览创建了全屏肖像。然而,这一行 您要纵向查看吗? mCamera.setDisplayOrientation(角度);引发错误,导致显示屏逆时针旋转 90 度。我通过将以下代码添加到 surfaceCreated() final int width = getWidth(); 来解决这个问题最终 int 高度 = getHeight();如果(高度 > 宽度) mCamera.setDisplayOrientation(90); mCamera.setPreviewDisplay(holder); mCamera.startPreview(); 启动相机预览时出错:设置显示方向失败 您使用的是哪个设备和安卓版本?

以上是关于相机预览人像设置参数失败的主要内容,如果未能解决你的问题,请参考以下文章

在 iOS OpenCV 相机中将相机预览旋转到人像

将相机预览旋转到人像 Android OpenCV 相机和 Xamarin

Android 相机启动预览失败

使用 PreviewView 来展示相机预览

Android 相机将无法工作。开始预览失败

如何在surfaceview中将Android相机更改为人像?