Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
730 views
in Technique[技术] by (71.8m points)

api - Android camera preview look strange

I am implementing a camera app and when I look at the preview (especially with front camera), the image is very fat. It looks like the image get stretched horizontally. I follow the sdk sample with the optimzed camera size but it doesn't help. How can I adjust my camera setting so that it will preview like the other camera app?

Thanks.

My code is below.

public class CameraActivity extends Activity implements SurfaceHolder.Callback, Camera.ShutterCallback, Camera.PictureCallback {

Camera m_camera;
SurfaceView m_surfaceView;
int m_numOfCamera;
int m_defaultCameraId;
int m_currentCamera;
int m_surfaceWidth;
int m_surfaceHeight;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_camera);
    getActionBar().setDisplayHomeAsUpEnabled(true);


    m_surfaceView = (SurfaceView)findViewById(R.id.cameraPreview);
    m_surfaceView.getHolder().addCallback(this);

    m_camera = Camera.open();

    m_numOfCamera = Camera.getNumberOfCameras();

    CameraInfo cameraInfo = new CameraInfo();
    for (int i = 0; i < m_numOfCamera; ++i) {
        Camera.getCameraInfo(i, cameraInfo);
        if (cameraInfo.facing == CameraInfo.CAMERA_FACING_BACK) {
            m_defaultCameraId = i;
            m_currentCamera = m_defaultCameraId;
        }       
    }

    if (m_numOfCamera < 1) {
        MenuItem switchCam = (MenuItem)findViewById(R.id.menu_switch_camera);
        switchCam.setVisible(false);
    }
}

@Override
public void onPause() {
    super.onPause();
    m_camera.stopPreview();
}

@Override
public void onDestroy() {
    super.onDestroy();
    m_camera.release();
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.activity_camera, menu);
    return true;
}

@Override
public boolean onOptionsItemSelected(final MenuItem item) 
{
    if (item.getItemId() == android.R.id.home) 
    {
        Intent intent = new Intent(this, MainActivity.class);
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        startActivity(intent);

        return true;
    } 
    else if (item.getItemId() == R.id.menu_switch_camera)
    {
        if (m_camera != null) {
            m_camera.stopPreview();
            m_camera.release();
            m_camera = null;
        }   

        m_camera = Camera.open((m_currentCamera + 1) % m_numOfCamera);
        m_currentCamera = (m_currentCamera + 1) % m_numOfCamera;

        Camera.Parameters params = m_camera.getParameters();
        List<Camera.Size> sizes = params.getSupportedPreviewSizes();
        Camera.Size size = getOptimalPreviewSize(sizes, m_surfaceWidth, m_surfaceHeight);

        params.setPreviewSize(size.width,  size.height);

        m_camera.setParameters(params);
        setCameraDisplayOrientation(this, m_currentCamera, m_camera);
        m_camera.startPreview();
        try {
            m_camera.setPreviewDisplay(m_surfaceView.getHolder());
        } catch (Exception e) {
            e.printStackTrace();
        }   
        return true;
    }
    return true;
}

public void onPictureTaken(byte[] arg0, Camera arg1) {
    // TODO Auto-generated method stub

}

public void onShutter() {
    // TODO Auto-generated method stub

}

public void surfaceChanged(SurfaceHolder arg0, int format, int w, int h) {
    m_surfaceWidth = w;
    m_surfaceHeight = h;
    Camera.Parameters params = m_camera.getParameters();
    List<Camera.Size> sizes = params.getSupportedPreviewSizes();
    Camera.Size selected = getOptimalPreviewSize(sizes, w, h);

    params.setPreviewSize(selected.width,  selected.height);


    m_camera.setParameters(params);
    setCameraDisplayOrientation(this, m_currentCamera, m_camera);
    m_camera.startPreview();    
}

private static void setCameraDisplayOrientation(Activity activity,
         int cameraId, android.hardware.Camera camera) {
     android.hardware.Camera.CameraInfo info =
             new android.hardware.Camera.CameraInfo();
     android.hardware.Camera.getCameraInfo(cameraId, info);
     int rotation = activity.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 the mirror
     } else {  // back-facing
         result = (info.orientation - degrees + 360) % 360;
     }
     camera.setDisplayOrientation(result);
 }


public void surfaceCreated(SurfaceHolder arg0) {
    try {
        m_camera.setPreviewDisplay(m_surfaceView.getHolder());
    } catch (Exception e) {
        e.printStackTrace();
    }   
}

public void surfaceDestroyed(SurfaceHolder arg0) {
    // TODO Auto-generated method stub

}

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

    return optimalSize;
}

}
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The camera preview always fills up the SurfaceView showing it. If the aspect ratio of m_surfaceView doesn't match with the camera's aspect ratio, the preview will be stretched.

You'll need to create m_surfaceView matching the aspect ratio. That means, you'll need to create it from code, not from layout XML file.

There is a sample project APIDemos that you'll find in android sample projects. In the project there is a thing named CameraPreview. This one has a good demonstration for setting up camera preview in a SurfaceView. It has a class that extends ViewGroup, and adds the SurfaceView as its child from the code. The onMeasure() method has been overridden to determine the height and width of the SurfaceView, so the aspect ratio is preserved. Take a look on the project, and I hope it will be clear.

[Sorry I couldn't post the link here - this is supposed to be the link, but I found it broken. But if you have installed the sample projects with the Android SDK, you can find the project in the samples. Open a new Android Sample Project, select APIDemos, then look for a class named CameraPreview. It should be in the package com.example.android.apis.graphics, as far as I remember.]


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...