Android Camera Preview Stretched


Answer :

I'm using this method -> based on API Demos to get my Preview Size:

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

As you can see you have to input width and height of your screen. This method will calculate screen ratio based on those values and then from the list of supportedPreviewSizes it will choose the best for you from avaliable ones. Get your supportedPreviewSize list in place where Camera object isn't null by using

mSupportedPreviewSizes = mCamera.getParameters().getSupportedPreviewSizes(); 

And then on in onMeasure you can get your optimal previewSize like that:

@Override     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {         final int width = resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec);         final int height = resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec);         setMeasuredDimension(width, height);          if (mSupportedPreviewSizes != null) {            mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, width, height);         }     } 

And then (in my code in surfaceChanged method, like I said I'm using API Demos structure of CameraActivity code, you can generate it in Eclipse):

Camera.Parameters parameters = mCamera.getParameters(); parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height); mCamera.setParameters(parameters); mCamera.startPreview(); 

And one hint for you, because I did almost the same app like you. Good practice for Camera Activity is to hide StatusBar. Applications like Instagram are doing it. It reduces your screen height value and change your ratio value. It is possible to get strange Preview Sizes on some devices (your SurfaceView will be cut a little)


And to answer your question, how to check if your preview ratio is correct? Then get height and width of parameters that you set in:

mCamera.setParameters(parameters); 

your set ratio is equal to height/width. If you want camera to look good on your screen then height/width ratio of parameters that you set to camera must be the same as height(minus status bar)/width ratio of your screen.


F1Sher's solution is nice but sometimes doesn't work. Particularly, when your surfaceView doesn't cover whole screen. In this case you need to override onMeasure() method. I have copied my code here for your reference.

Since I measured surfaceView based on width then I have little bit white gap at the end of my screen that I filled it by design. You are able to fix this issue if you keep height and increase width by multiply it to ratio. However, it will squish surfaceView slightly.

public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {      private static final String TAG = "CameraPreview";      private Context mContext;     private SurfaceHolder mHolder;     private Camera mCamera;     private List<Camera.Size> mSupportedPreviewSizes;     private Camera.Size mPreviewSize;      public CameraPreview(Context context, Camera camera) {         super(context);         mContext = context;         mCamera = camera;          // supported preview sizes         mSupportedPreviewSizes = mCamera.getParameters().getSupportedPreviewSizes();         for(Camera.Size str: mSupportedPreviewSizes)                 Log.e(TAG, 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);     }      public void surfaceCreated(SurfaceHolder holder) {         // empty. surfaceChanged will take care of stuff     }      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) {         Log.e(TAG, "surfaceChanged => w=" + w + ", h=" + 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 parameters = mCamera.getParameters();             parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);             mCamera.setParameters(parameters);             mCamera.setDisplayOrientation(90);             mCamera.setPreviewDisplay(mHolder);             mCamera.startPreview();          } catch (Exception e){             Log.d(TAG, "Error starting camera preview: " + e.getMessage());         }     }      @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;     } } 

NOTE: MY SOLUTION IS A CONTINUATION OF HESAM'S SOLUTION: https://stackoverflow.com/a/22758359/1718734

What I address: Hesam's said there is a little white space that may appear on some phones, like this:

NOTE: Although the aspect ratio is correct, the camera does not fill in the whole screen.

Hesam suggested a second solution, but that squishes the preview. And on some devices, it heavily distorts.

So how do we fix this problem. It is simple...by multiplying the aspect ratios till it fills in the screen. I have noticed, several popular apps such as Snapchat, WhatsApp, etc works the same way.

All you have to do is add this to the onMeasure method:

float camHeight = (int) (width * ratio);     float newCamHeight;     float newHeightRatio;      if (camHeight < height) {         newHeightRatio = (float) height / (float) mPreviewSize.height;         newCamHeight = (newHeightRatio * camHeight);         Log.e(TAG, camHeight + " " + height + " " + mPreviewSize.height + " " + newHeightRatio + " " + newCamHeight);         setMeasuredDimension((int) (width * newHeightRatio), (int) newCamHeight);         Log.e(TAG, mPreviewSize.width + " | " + mPreviewSize.height + " | ratio - " + ratio + " | H_ratio - " + newHeightRatio + " | A_width - " + (width * newHeightRatio) + " | A_height - " + newCamHeight);     } else {         newCamHeight = camHeight;         setMeasuredDimension(width, (int) newCamHeight);         Log.e(TAG, mPreviewSize.width + " | " + mPreviewSize.height + " | ratio - " + ratio + " | A_width - " + (width) + " | A_height - " + newCamHeight);     } 

This will calculate the screen height and gets the ratio of the screen height and the mPreviewSize height. Then it multiplies the camera's width and height by the new height ratio and the set the measured dimension accordingly.

enter image description here

And the next thing you know, you end up with this :D

enter image description here

This also works well with he front camera. I believe this is the best way to go about this. Now the only thing left for my app is to save the preview itself upon clicking on "Capture." But ya, this is it.


Comments

Popular posts from this blog

Converting A String To Int In Groovy

"Cannot Create Cache Directory /home//.composer/cache/repo/https---packagist.org/, Or Directory Is Not Writable. Proceeding Without Cache"

Android How Can I Convert A String To A Editable