0

I have a camera preview at the top of the screen with a specific height and width. I am using front camera to generate the preview. The camera is working but the issue is the preview shows the bottom part of what the camera sees.

Check out the screenshot

I am holding the camera right in front of the face and it should show the entire face.

This is my surfaceview class:

 public class MyCameraSurfaceView extends SurfaceView implements SurfaceHolder.Callback {

    private SurfaceHolder mHolder;
    private Camera mCamera;
    private List<Camera.Size> mSupportedPreviewSizes;
    private Camera.Size mPreviewSize;

    public MyCameraSurfaceView(Context context, Camera camera) {
        super(context);
        mCamera = camera;


        mSupportedPreviewSizes = mCamera.getParameters().getSupportedPreviewSizes();
          for(Camera.Size str: mSupportedPreviewSizes)
            Log.e("Sizes", str.width + "/" + str.height);

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

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

        Log.e("Surface changed", "surfaceChanged => w=" + weight + ", h=" + height);

        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
        }

        // make any resize, rotate or reformatting changes here

        // start preview with new settings
        try {
            Camera.Parameters parameters = mCamera.getParameters();
          //  parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
            parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);

            mCamera.setParameters(parameters);
            mCamera.setPreviewDisplay(mHolder);
            //mediaRecorder.setOrientationHint(90);
            mCamera.setDisplayOrientation(90);
            mCamera.startPreview();

        } catch (Exception e) {
        }
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        // TODO Auto-generated method stub
        // The Surface has been created, now tell the camera where to draw the preview.
        try {
            mCamera.setPreviewDisplay(holder);
            mCamera.setDisplayOrientation(90);
            mCamera.startPreview();
        } catch (IOException e) {
        }
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        // TODO Auto-generated method stub

    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        final int width = resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec);
        final int height = resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec);

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

        if (mPreviewSize != null) {
            float ratio;
            if (mPreviewSize.height >= mPreviewSize.width)
                ratio = (float) mPreviewSize.height / (float) mPreviewSize.width;
            else
                ratio = (float) mPreviewSize.width / (float) mPreviewSize.height;

            // One of these methods should be used, second method squishes preview slightly
            setMeasuredDimension(width, (int) (width * ratio));
            //        setMeasuredDimension((int) (width * ratio), height);
        }
    }

    private Camera.Size getOptimalPreviewSize(List<Camera.Size> sizes, int w, int h) {
        final double ASPECT_TOLERANCE = 0.1;
        double targetRatio = (double) h / w;

        if (sizes == null)
            return null;

        Camera.Size optimalSize = null;

        double minDiff = Double.MAX_VALUE;

        int targetHeight = h;

        for (Camera.Size size : sizes) {
            double ratio = (double) size.height / size.width;
            if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE)
                continue;

            if (Math.abs(size.height - targetHeight) < minDiff) {
                optimalSize = size;
                minDiff = Math.abs(size.height - targetHeight);
            }
        }

        if (optimalSize == null) {
            minDiff = Double.MAX_VALUE;
            for (Camera.Size size : sizes) {
                if (Math.abs(size.height - targetHeight) < minDiff) {
                    optimalSize = size;
                    minDiff = Math.abs(size.height - targetHeight);
                }
            }
        }

        return optimalSize;
    }

}

And xml:

 <FrameLayout
    android:id="@+id/videoview"
    android:layout_width="match_parent"
    android:layout_height="200dp"
    android:layout_marginTop="10dp"
    android:layout_marginLeft="10dp"
    android:layout_marginRight="10dp"

    >
</FrameLayout>

I tried manually putting values to parameters.setPreviewSize(300,200) but it just shows a black screen in the preview. I am new to camera API in android and unfortunately I am running short of time to read google docs to understand the principle of camera api and manipulate my code accordingly. Anny help would be appreciated.

Abhinav Raja
  • 349
  • 1
  • 5
  • 25

2 Answers2

0

You can try to change the setMeasuredDimension params into onMeasure method.

  • It is doing the job but compressing the preview. Its like it compresses the the entire height what camera sees to the mentioned height in params. – Abhinav Raja Sep 08 '17 at 15:25
0

You cannot hardcode 300×200 because you can only choose one of supported preview sizes.But in your case, the width may different for different devices, and the aspect ratio too.

Actually, choosing 300×200 is not good for another reason: even though your preview height is hardcoded to 200, it is 200dp, but the camera preview works with real pixels. The best result (no pixelization) is achieved when your preview size is not less than the (real pixel) size of the preview surface on the screen.

Your code for MyCameraSurfaceView class looks good. The question is how you insert it into the videoview frame. You should set the layout parameters to Gravity.CENTER. This will make your preview equally cropped from top and bottom. If you want to send all pixels to display, you can choose to either distort the image, or to keep margins (similar to how youtube shows narrow videos).

Alex Cohn
  • 56,089
  • 9
  • 113
  • 307
  • setting parameters to Gravity.center is not doing anything. I just need the camera preview to show little down than what it shows now. Can this be achieved by doing anything else? – Abhinav Raja Sep 09 '17 at 15:04
  • You probably did gravity incorrectly. Can you disclose your code? – Alex Cohn Sep 09 '17 at 15:46
  • Well there was another way I resolved it. I increased the height of my preview and its showing a good preview. Another issue I am having is when I start recording the preview changes(the resolution is adjusted) and preview appears stretched. Do you know why this happens? Is the surface getting recreated? If so, how can I maintain the same preview even after the mediarecording starts? – Abhinav Raja Sep 09 '17 at 18:03
  • You set the size for mediarecorder separately. Make sure that you use the same size. – Alex Cohn Sep 10 '17 at 05:39
  • I tried using multiple combinations. The preview gets distorted when recording starts. Is it possible to keep the existing camera preview on the surface and start mediarecording without using mediaRecorder.setPreviewDisplay()? Coz according to google docs you dont need to setpreview display if your surface is non null. – Abhinav Raja Sep 10 '17 at 09:38
  • Sure you don't need to call `MediaRecorder.setPreviewDisplay()`. – Alex Cohn Sep 10 '17 at 09:42
  • It gives an illegalstateException when mediarecorder starts. I think my issue is I need to find a preview size and video size from the list of supported sizes that matches the dimensions of the surfaceview. – Abhinav Raja Sep 10 '17 at 10:14