6

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

}
Kintarō
  • 2,947
  • 10
  • 48
  • 75

4 Answers4

16

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.]

Sufian Latif
  • 13,086
  • 3
  • 33
  • 70
1

I changed onLayout method and not camera preview is not stretched. Rest of the thing are same like APiDemo which find here sdk/sample/adroid-18.The idea is we have only some supported size of preview but our view size may not always match with preview size. so i took larger preview size then my imageview size. it works for me. May help someone..

@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;

                left = (width - scaledChildWidth) / 2;
                top = 0;
                right = (width + scaledChildWidth) / 2;
                bottom = height;

                child.layout(left, top, right, bottom);
            } else {
                final int scaledChildHeight = previewHeight * width
                        / previewWidth;

                left = 0;
                top = (height - scaledChildHeight) / 2;
                right = width;
                bottom = (height + scaledChildHeight) / 2;

                child.layout(left, top, right, bottom);
            }
        }
    }
Akanksha Rathore
  • 3,603
  • 3
  • 31
  • 47
0

I have problem with too stretching camera preview. It is too stretching at vertical and landscape mode.

So at manifest i added screenOrentation="Portrait", but it did not help,still preview is rescaled at any position (vertical - preview is to wide or landscape is too long) you can see this at screens. I would like to add at Samsung ace III everything is fine but at LG Nexus 4 is stretching

 package pl.probs.camera.component;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.graphics.Point;
import android.hardware.Camera;
import android.hardware.Camera.AutoFocusCallback;
import android.hardware.Camera.CameraInfo;
import android.hardware.Camera.Parameters;
import android.hardware.Camera.Size;
import android.util.Log;
import android.view.Display;
import android.view.MotionEvent;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import pl.probs.lib.debug.L;

public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
    private static final String TAG = "CameraPreview";
    private static boolean showLogs = true;
    private SurfaceHolder mHolder;
    private Camera mCamera;
    private Context context;
    private Parameters resolution;
    private List<Size> lSuportedPreviewSize;
    private static int cOrientation = 0; // aktualny kat orientacji
    private static boolean cOrientationChanged = false; // Stan orientacji
                                                        // zostal zmieniony
                                                        // wzgledem poprzedniego
    private Display display; // Rozmiar ekranu
    private Point displaySize; // Zmienna przechowuje Rozmiar Ekranu
    private Point optimalPreviewSize;
public CameraPreview(Context context, Camera camera, int resolution) {
    super(context);
    this.optimalPreviewSize = new Point();
    this.context = context;
    this.mCamera = camera;
    setDisplaySize(this.display);
    setFocusable(true);
    setFocusableInTouchMode(true);
    mHolder = getHolder();
    mHolder.addCallback(this);
    mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    this.resolution = getMinResolution(resolution);
    this.optimalPreviewSize = getOptimalPreviewResolution(this.display);
    Size s = mCamera.getParameters().getPreviewSize(); // Sprawdzenie jaki
                                                        // prewiev ustawiony
}

public Point getOptimalPreviewSize() {
    return optimalPreviewSize;
}

public void surfaceCreated(SurfaceHolder holder) {
    try {
        if (mCamera != null) {
            mCamera.stopPreview();
            mCamera.setPreviewDisplay(holder);
            Size s = mCamera.getParameters().getPreviewSize();
            mCamera.startPreview();
        }
    } catch (IOException e) {
        L.d("Błąd ustawiania podglÄ…du: " + e.getMessage());
    }
}

protected void onPause() {
    // Because the Camera object is a shared resource, it's very
    // important to release it when the activity is paused.
    if (mCamera != null) {
        mCamera.release();
        mCamera = null;
    }
}

public void surfaceDestroyed(SurfaceHolder holder) {
    if (mCamera != null) {
        mCamera.stopPreview();
    }
}

public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
    Camera.Parameters setPrevOrientation = mCamera.getParameters();
    if (mHolder.getSurface() == null)
        return;

    try {
        mCamera.stopPreview();
        Size sizeBefore = mCamera.getParameters().getPreviewSize();
        setPrevOrientation.setRotation(setCameraDisplayOrientation((Activity) context, getCameraId(), mCamera));
        // Orientacja Portrait np 640x480 Landscape 480x640
        this.resolution.setPreviewSize(this.optimalPreviewSize.x, this.optimalPreviewSize.y);
        mCamera.setParameters(this.resolution);
        Size sizeAfter = mCamera.getParameters().getPreviewSize();
    } catch (RuntimeException e) {
        L.d("Podgląd nie istnieje");
    }

    try {
        mCamera.stopPreview();
        mCamera.setPreviewDisplay(mHolder);
        mCamera.startPreview();

    } catch (Exception e) {
        L.d("błąd podgladu: " + e.getMessage());
    }
}

@SuppressLint("ClickableViewAccessibility")
public boolean onTouchEvent(MotionEvent event) {
    if (event.getAction() == MotionEvent.ACTION_DOWN) {
        mCamera.autoFocus(new AutoFocusCallback() {

            @Override
            public void onAutoFocus(boolean success, Camera camera) {
                // do something
            }
        });

    }

    return true;
}

private static int 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;
    }

    cOrientation = degrees;

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

private int getCameraId() {
    int cameraId = -1;
    int numberOfCameras = Camera.getNumberOfCameras();
    for (int i = 0; i < numberOfCameras; i++) {
        CameraInfo info = new CameraInfo();
        Camera.getCameraInfo(i, info);
        if (info.facing == CameraInfo.CAMERA_FACING_BACK) {
            cameraId = i;
            break;
        }
    }
    return cameraId;
}

private Parameters getMinResolution(int desireResolutionInMpx) {
    int height[], width[], size;
    float megapixels;
    Camera.Parameters p = mCamera.getParameters();
    size = p.getSupportedPictureSizes().size();
    height = new int[size];
    width = new int[size];
    for (int i = 0; i < size; i++) {
        height[i] = p.getSupportedPictureSizes().get(i).height;
        width[i] = p.getSupportedPictureSizes().get(i).width;
        megapixels = (float) (((float) height[i] * (float) width[i]) / 1024000);
        if (megapixels <= desireResolutionInMpx) {
            p.setPictureSize(width[i], height[i]);
            break;
        }
    }
    return p;
}

private Point getOptimalPreviewResolution(Display displaySize) {
    lSuportedPreviewSize = mCamera.getParameters().getSupportedPreviewSizes();
    Point optimalPreviewSize = new Point();
    int displayWidth = displaySize.getWidth(); // szerokosc ekranu
    int displayHeight = displaySize.getHeight(); // wysokosc ekranu
    int cameraHeight; // wspierana wysokosc kamery
    int cameraWidth; // wspierana szerokosc kamery

    // Lista przechowywujace SupportedPreviewSize kamery, wszyskie
    // rozdzielczosci mniejsze od szerokosc i wysokosci ekranu
    List<Point> lOptimalPoint = new ArrayList<Point>();

    // Pomocniczo do listowania zawartosci listy
    // TODO manta displayHeight cameraHeight brak oraz width brak zgodnosci
    // (
    System.out.println(lOptimalPoint.toString());
    for (int i = 0; i < lSuportedPreviewSize.size(); i++) {
        Log.i(TAG, "w " + lSuportedPreviewSize.get(i).width + " h " + lSuportedPreviewSize.get(i).height + " \n");
    }

    // Wyszukanie wszystkich wysokosci kamery mniejszej od wysokosci ekranu
    for (int i = 0; i < lSuportedPreviewSize.size(); i++) {
        // TODO Uwazaj kamera zapisuje swoj rozmiar dla pozycji landscape
        // gdzie height = 480 a width = 800
        cameraHeight = lSuportedPreviewSize.get(i).width;
        cameraWidth = lSuportedPreviewSize.get(i).height;
        // Porownaj wysokosc ekranu urzadzenia z wysokosci supportedPreview
        // dodaj do listy
        if (displayHeight > cameraHeight) {
            lOptimalPoint.add(new Point(cameraHeight, cameraWidth));
        }
    }

    // Sortowanie rosnaco
    Collections.sort(lOptimalPoint, new ComapreSupportedPreviewByWidth());
    // Ostatni element listy optymalny
    optimalPreviewSize = lOptimalPoint.get(lOptimalPoint.size()-1);

    // Zwracana rozdzielczosc landscape aparatu np (800x600)
    return optimalPreviewSize;
}

private void setDisplaySize(Display display) {
    Activity activity = (Activity) this.context; // Pobierz aktywnosc aby
                                                    // znać rozmiar ekranu

    this.display = activity.getWindowManager().getDefaultDisplay();
}

class ComapreSupportedPreviewByWidth implements Comparator<Point> {

    @Override
    public int compare(Point lhs, Point rhs) {
        return lhs.x - rhs.x;
    }
}

}

Link to screens and project doing at eclipse

0

I already solved problem. What could cause problem with strange camera preview.

  • Status bar takes spaces - you can hide it Hiding the Status Bar

  • Some spaces also take TitleBar - you can turn off this at manifest

android:theme="@android:style/Theme.NoTitleBar">

  • Changed activity orientation to landscape "beacuse camera preview support that orientation" - you can check this at API demo Graphics->CameraPreview

  • algorithm that compares the size of Display.getWidth () Camera.getParameters size (). getSupportedPreviewSizes (); if they are the same is a function of surfaceChanged change Parametrs.setPreviewSize (x, y) you received when searching the list


public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
    Camera.Parameters setPrevOrientation = mCamera.getParameters();
    if (mHolder.getSurface() == null)
        return;

    try {
        mCamera.stopPreview();
        Size sizeBefore = mCamera.getParameters().getPreviewSize();
        setPrevOrientation.setRotation(setCameraDisplayOrientation(
                (Activity) context, getCameraId(), mCamera));
        // Orientacja Portrait np 640x480 Landscape 480x640
        this.resolution.setPreviewSize(this.optimalPreviewSize.x,
                this.optimalPreviewSize.y);
        mCamera.setParameters(this.resolution);
        Size sizeAfter = mCamera.getParameters().getPreviewSize();
    } catch (RuntimeException e) {

        L.d("Podgląd nie istnieje");
    }

    try {
        mCamera.stopPreview();
        mCamera.setPreviewDisplay(mHolder);
        mCamera.startPreview();

    } catch (Exception e) {
        L.d("błąd podgladu: " + e.getMessage());
    }
 } 

    private Point getOptimalPreviewResolution(Display displaySize) {

        lSuportedPreviewSize = mCamera.getParameters()
                .getSupportedPreviewSizes();
        Point optimalPreviewSize = new Point();
        int displayWidth = displaySize.getWidth();
        int displayHeight = displaySize.getHeight();
        int cameraHeight;
        int cameraWidth;

        List<Point> lOptimalPoint = new ArrayList<Point>();

        for (int i = 0; i < lSuportedPreviewSize.size(); i++) {
            cameraHeight = lSuportedPreviewSize.get(i).width;
            cameraWidth = lSuportedPreviewSize.get(i).height;
            if (displayHeight >= cameraHeight) {
                lOptimalPoint.add(new Point(cameraHeight, cameraWidth));
            }
        }

        // Sort ascending
        Collections.sort(lOptimalPoint,
                new ComapreSupportedPreviewByWidth());
        // Last element is optimal
        optimalPreviewSize = lOptimalPoint.get(lOptimalPoint.size() - 1);

        // Return resolution - camera at landscape mode (800x600)
        return optimalPreviewSize;
    }

    class ComapreSupportedPreviewByWidth implements Comparator<Point> {

    @Override
    public int compare(Point lhs, Point rhs) {
        return lhs.x - rhs.x;
    }