i created a camera app based on tutorial. the preview class i use is from api-Demos "CameraPreview". I added a modification from here (preview was always rotated by 90°). So this is how i set preview size:
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
// Now that the size is known, set up the camera parameters and begin
// the preview.
Camera.Parameters parameters = mCamera.getParameters();
Display display = ((WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
if (display.getRotation() == Surface.ROTATION_0) {
parameters.setPreviewSize(mPreviewSize.height, mPreviewSize.width);
mCamera.setDisplayOrientation(90);
}
if (display.getRotation() == Surface.ROTATION_90) {
parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
}
if (display.getRotation() == Surface.ROTATION_180) {
parameters.setPreviewSize(mPreviewSize.height, mPreviewSize.width);
}
if (display.getRotation() == Surface.ROTATION_270) {
parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
mCamera.setDisplayOrientation(180);
}
parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
requestLayout();
mCamera.setParameters(parameters);
mCamera.startPreview();
}
But the Preview is displayed with wrong aspect ratio. Is it because of the code above or probably because of the layout i use?:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<Button
android:id="#+id/button_capture"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:text="#string/capture" />
<FrameLayout
android:id="#+id/camera_preview"
android:layout_width="100dp"
android:layout_height="match_parent"/>
So how to get the correct aspect ratio? Thanks in advance.
P.S. i read the answer from: Android camera preview look strange
But this isn't working for me.
Try changing the preview sizes with adding this function:
private Camera.Size getOptimalPreviewSize(List<Camera.Size> sizes, int w, int h) {
final double ASPECT_TOLERANCE = 0.05;
double targetRatio = (double) w/h;
if (sizes==null) return null;
Camera.Size optimalSize = null;
double minDiff = Double.MAX_VALUE;
int targetHeight = h;
// Find size
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;
}
And the setting the sizes from these optimized values:
List<Camera.Size> sizes = parameters.getSupportedPreviewSizes();
Camera.Size optimalSize = getOptimalPreviewSize(sizes, getResources().getDisplayMetrics().widthPixels, getResources().getDisplayMetrics().heightPixels);
parameters.setPreviewSize(optimalSize.width, optimalSize.height);
The following code modifies the width/height of the camera preview container to match the aspect ratio of the camera preview.
Camera.Size size = camera.getParameters().getPreviewSize();
//landscape
float ratio = (float)size.width/size.height;
//portrait
//float ratio = (float)size.height/size.width;
preview = (FrameLayout) findViewById(R.id.camera_preview);
int new_width=0, new_height=0;
if(preview.getWidth()/preview.getHeight()<ratio){
new_width = Math.round(preview.getHeight()*ratio);
new_height = cameraPreview.getHeight();
}else{
new_width = preview.getWidth();
new_height = Math.round(preview.getWidth()/ratio);
}
preview.setLayoutParams(new FrameLayout.LayoutParams(new_width, new_height));
Using the above solution, using the method private Size getOptimalPreviewSize(List sizes, int w, int h).
Worked fine! I was having problems with the aspect ratio on portrait orientation: Here it´s my solution using. Mixing it with android's documentation:
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
Camera.Parameters params = mCamera.getParameters();
params.set("orientation", "portrait");
Size optimalSize=getOptimalPreviewSize(params.getSupportedPreviewSizes(), getResources().getDisplayMetrics().widthPixels, getResources().getDisplayMetrics().heightPixels);
params.setPreviewSize(optimalSize.width, optimalSize.height);
mCamera.setParameters(params);
// start preview with new settings
try {
mCamera.setPreviewDisplay(mHolder);
mCamera.startPreview();
} catch (Exception e){
Log.d(TAG, "Error starting camera preview: " + e.getMessage());
}
}
}
problem is really in way you layout things.
there is overriden onLayout in Preview class. idea of its work is to set child SurfaceView size according to found optimal Size. but it doesn't take rotation into account, so you need to do it by yourself:
if (mPreviewSize != null) {
previewWidth = mPreviewSize.height;
previewHeight = mPreviewSize.width;
}
instead of
if (mPreviewSize != null) {
previewWidth = mPreviewSize.width;
previewHeight = mPreviewSize.height;
}
Trick is to swap width and height which is done because of 90 degree rotation achieved by
mCamera.setDisplayOrientation(90);
You might also consider forking child preview size setting depending on orientation that you set in
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
//...
}
(in provided by me code it is always for 90 degree rotation, for 180 you don't have to do anyhing and when you don't set any rotation, there is no need to swap width and height)
Another thing worth mentioning - when calculating getOptimalPreviewSize for case when you have rotation and you swap child width and height you also should pass parent(Preview) width and height swapped in onMeasure:
if (mSupportedPreviewSizes != null) {
//noinspection SuspiciousNameCombination
final int previewWidth = height;
//noinspection SuspiciousNameCombination
final int previewHeight = width;
mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, previewWidth, previewHeight);
}
Henric's answer didn't work for me, so I've created another method which determines the optimal preview size for any camera given the target view current width and height and also the activity orientation:
public static Size getOptimalPreviewSize(List<Camera.Size> cameraPreviewSizes, int targetWidth, int targetHeight, boolean isActivityPortrait) {
if (CommonUtils.isEmpty(cameraPreviewSizes)) {
return null;
}
int optimalHeight = Integer.MIN_VALUE;
int optimalWidth = Integer.MIN_VALUE;
for (Camera.Size cameraPreviewSize : cameraPreviewSizes) {
boolean isCameraPreviewHeightBigger = cameraPreviewSize.height > cameraPreviewSize.width;
int actualCameraWidth = cameraPreviewSize.width;
int actualCameraHeight = cameraPreviewSize.height;
if (isActivityPortrait) {
if (!isCameraPreviewHeightBigger) {
int temp = cameraPreviewSize.width;
actualCameraWidth = cameraPreviewSize.height;
actualCameraHeight = temp;
}
} else {
if (isCameraPreviewHeightBigger) {
int temp = cameraPreviewSize.width;
actualCameraWidth = cameraPreviewSize.height;
actualCameraHeight = temp;
}
}
if (actualCameraWidth > targetWidth || actualCameraHeight > targetHeight) {
// finds only smaller preview sizes than target size
continue;
}
if (actualCameraWidth > optimalWidth && actualCameraHeight > optimalHeight) {
// finds only better sizes
optimalWidth = actualCameraWidth;
optimalHeight = actualCameraHeight;
}
}
Size optimalSize = null;
if (optimalHeight != Integer.MIN_VALUE && optimalWidth != Integer.MIN_VALUE) {
optimalSize = new Size(optimalWidth, optimalHeight);
}
return optimalSize;
}
This uses a custom Size object, because Android's Size is available after API 21.
public class Size {
private int width;
private int height;
public Size(int width, int height) {
this.width = width;
this.height = height;
}
public int getHeight() {
return height;
}
public int getWidth() {
return width;
}
}
You can determine the width and height of a view by listening for its global layout changes and then you can set the new dimensions. This also shows how to programmatically determine activity orientation:
cameraPreviewLayout.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
// gets called after layout has been done but before display.
cameraPreviewLayout.getViewTreeObserver().removeGlobalOnLayoutListener(this);
boolean isActivityPortrait = getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;
Size optimalCameraPreviewSize = CustomUtils.getOptimalPreviewSize(cameraPreview.getCameraSizes(), cameraPreviewLayout.getWidth(), cameraPreviewLayout.getHeight(), isActivityPortrait);
if (optimalCameraPreviewSize != null) {
LinearLayout.LayoutParams cameraPreviewLayoutParams = new LinearLayout.LayoutParams(optimalCameraPreviewSize.getWidth(), optimalCameraPreviewSize.getHeight());
cameraPreviewLayout.setLayoutParams(cameraPreviewLayoutParams);
}
}
});
Related
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.
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.
You can try to change the setMeasuredDimension params into onMeasure method.
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).
I'm facing an issue with Camera.setParameters, below is what I noticed,
On Nexus the SurfaceView(w, h) is 1080-1776 and the optimal size is W:1600-H:1200, it's working fine when I set the parameters
--------Nexus--------
SurfaceView(w, h) 1080-1776
Available cam sizes
size 1920-1080
size 1600-1200
size 1280-960
size 1280-768
size 1280-720
size 1024-768
size 800-600
size 800-480
size 720-480
size 640-480
size 352-288
size 320-240
size 176-144
optimalSize 1600-1200
==================================================================================================
The below is on Samsung, and it's crashing here, I already tried switching the W with H in the Camera.setParameters and it crahsed as well, I tried evey single size available and all of them crahsed
--------Samsung Back camera--------
SurfaceView(w, h) 1080-1920
Available cam sizes
size 1920-1080
size 1440-1080
size 1280-720
size 1056-864
size 960-720
size 720-480
size 640-480
size 320-240
size 176-144
optimalSize 1920-1080
And here the confusing part,
When I try the Camera.open(1) instead of Camera.open(0), the fron-facing camera, it works!!
--------Samsung front-facing camera--------
SurfaceView(w, h) 1080-1920
Available cam sizes
size 1920-1080
size 1440-1080
size 1280-720
size 1056-864
size 960-720
size 720-480
size 640-480
size 320-240
size 176-144
optimalSize 1920-1080
And the code I'm using is below, can some please tell me what's going on
class Preview extends ViewGroup implements SurfaceHolder.Callback {
private final String TAG = "Preview";
SurfaceView mSurfaceView;
SurfaceHolder mHolder;
Camera.Size mPreviewSize;
List<Camera.Size> mSupportedPreviewSizes;
Camera mCamera;
Context mContext;
int screenWidth;
int screenHeight;
Preview(Context context, SurfaceView sv, int width, int height) {
super(context);
mSurfaceView = sv;
// addView(mSurfaceView);
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();
// get Camera parameters
Camera.Parameters params = mCamera.getParameters();
List<String> focusModes = params.getSupportedFocusModes();
// if (focusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO)) {
// // set the focus mode
// params.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
// // set Camera parameters
// mCamera.setParameters(params);
// }
}
}
#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);
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 surfaceCreated(SurfaceHolder holder) {
// The Surface has been created, acquire the camera and tell it where
// to draw.
try {
if (mCamera != null) {
mCamera.setPreviewDisplay(holder);
}
} catch (IOException exception) {
Log.e(TAG, "IOException caused by setPreviewDisplay()", exception);
}
}
public void surfaceDestroyed(SurfaceHolder holder) {
// Surface will be destroyed when we return, so stop the preview.
if (mCamera != null) {
mCamera.stopPreview();
}
}
private Camera.Size getOptimalPreviewSize(List<Camera.Size> sizes, int w, int h) {
final double ASPECT_TOLERANCE = 0.1;
double targetRatio = (double) w / h;
if (sizes == null) return null;
Camera.Size optimalSize = null;
double minDiff = Double.MAX_VALUE;
int targetHeight = h;
for (Camera.Size size : sizes) {
Log.d("CamSettings", "size "+ size.width +"-"+ size.height);
}
// Try to find an size match aspect ratio and size
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);
}
}
// Cannot find the one match the aspect ratio, ignore the requirement
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);
}
}
}
Log.d("CamSettings", "optimalSize "+ optimalSize.width +"-"+ optimalSize.height);
return optimalSize;
}
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
if(mCamera != null) {
mCamera.setDisplayOrientation(90);
Camera.Parameters parameters = mCamera.getParameters();
Log.d("CamSettings", "SurfaceView " + w + "-" + h);
Camera.Size sc = getOptimalPreviewSize(parameters.getSupportedPreviewSizes(), w, h);
parameters.setPreviewSize(sc.width, sc.height);
parameters.setPictureSize(sc.width, sc.height);
// Camera.Size captureSize = determineBestPictureSize(parameters);
requestLayout();
mCamera.setParameters(parameters);
mCamera.startPreview();
}
}
And the crash is this
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.camtest1, PID: 21832
java.lang.RuntimeException: setParameters failed
at android.hardware.Camera.native_setParameters(Native Method)
at android.hardware.Camera.setParameters(Camera.java:1701)
at com.example.camtest1.Preview.surfaceChanged(Preview.java:174)
at android.view.SurfaceView.updateWindow(SurfaceView.java:611)
at android.view.SurfaceView.access$000(SurfaceView.java:94)
at android.view.SurfaceView$3.onPreDraw(SurfaceView.java:183)
at android.view.ViewTreeObserver.dispatchOnPreDraw(ViewTreeObserver.java:895)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2222)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1267)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6638)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:813)
at android.view.Choreographer.doCallbacks(Choreographer.java:613)
at android.view.Choreographer.doFrame(Choreographer.java:583)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:799)
at android.os.Handler.handleCallback(Handler.java:733)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:146)
at android.app.ActivityThread.main(ActivityThread.java:5635)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1291)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1107)
at dalvik.system.NativeStart.main(Native Method)
---Update---
I just tried disable this
parameters.setPictureSize(sc.width, sc.height)
and it's not crashing anymore, still trying to see if that is going to affect the aspect ratio
And here the confusing part,
When I try the Camera.open(1) instead of Camera.open(0), the
fron-facing camera, it works!!
Here is your answer right here.
When you are using Camera.open() it got an overloading method that except camera id Camera.open(int cameraId). For each camera id it got it's own set of supported sized, which is only logical as the front and the back camera got different resolutions. This is why your code is working with Camera.open(0) and not with Camera.open(1)
Use those methods to find the right settings for you after obtaining reference to camera.
camera.getParameters().getSupportedVideoSizes();
camera.getParameters().getSupportedPictureSizes();
camera.getParameters().getSupportedPreviewSizes()
Disabling the solved my issue.
parameters.setPictureSize(sc.width, sc.height)
It looks like that the sizes I got was for the preview, I wonder how to get/set the size of the picture!
You can also get supported picture sizes like you do on preview sizes.
getSupportedPictureSizes() method will return a list of Camera.Size's that is supported by the device's camera. And you can pass this list to the getOptimalPreviewSize. If you want preview and photo to have the same view angle, you can give parameters to getOptimalPreviewSize as getSupportedPictureSizes, yourPreviewWidth, yourPreviewHeight.
So it will return a consistent picture size to your preview.
I customize the camera view of Zbar API in ZBarScannerActivity class. This is the code i added.
setContentView(R.layout.scanner);
mAutoFocusHandler = new Handler();
// Create and configure the ImageScanner;
setupScanner();
// Create a RelativeLayout container that will hold a SurfaceView,
// and set it as the content of our activity.
mPreview = new CameraPreview(this, this, autoFocusCB);
LinearLayout zbarLayout = (LinearLayout) findViewById(R.id.zbar_layout_area);
mPreview.setLayoutParams(new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
zbarLayout.addView(mPreview);
Then when i run the app the camera preview is wrong so i rotate it using setDisplayOrientation to 90. Then its already rotate but the camera view is stretched. This is my CameraPreview code.
private final String TAG = "CameraPreview";
SurfaceView mSurfaceView;
SurfaceHolder mHolder;
Size mPreviewSize;
List<Size> mSupportedPreviewSizes;
Camera mCamera;
PreviewCallback mPreviewCallback;
AutoFocusCallback mAutoFocusCallback;
CameraPreview(Context context, PreviewCallback previewCallback, AutoFocusCallback autoFocusCb) {
super(context);
mPreviewCallback = previewCallback;
mAutoFocusCallback = autoFocusCb;
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();
}
}
#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);
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 hideSurfaceView() {
mSurfaceView.setVisibility(View.INVISIBLE);
}
public void showSurfaceView() {
mSurfaceView.setVisibility(View.VISIBLE);
}
public void surfaceCreated(SurfaceHolder holder) {
// The Surface has been created, acquire the camera and tell it where
// to draw.
try {
if (mCamera != null) {
mCamera.setPreviewDisplay(holder);
}
} catch (IOException exception) {
Log.e(TAG, "IOException caused by setPreviewDisplay()", exception);
}
}
public void surfaceDestroyed(SurfaceHolder holder) {
// Surface will be destroyed when we return, so stop the preview.
if (mCamera != null) {
mCamera.cancelAutoFocus();
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) {
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.d("Edgar", optimalSize.width + " " + optimalSize.height);
return optimalSize;
}
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
if (holder.getSurface() == null){
// preview surface does not exist
return;
}
if (mCamera != null) {
// Now that the size is known, set up the camera parameters and begin
// the preview.
Camera.Parameters parameters = mCamera.getParameters();
parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
requestLayout();
mCamera.setDisplayOrientation(90);
mCamera.setParameters(parameters);
mCamera.setPreviewCallback(mPreviewCallback);
mCamera.startPreview();
mCamera.autoFocus(mAutoFocusCallback);
}
}
Please help me to fix the camera view. Thank you for helping.
If you are trying to do a view in portrait view, but basically show a landscape view portal that is not 'squished' then I would suggest doing a layout where the preview is behind your other layouts. Basically you could do
<RelativeLayout
android:layout_height="fill_parent"
android:layout_width="fill_parent"
>
<FrameLayout
android:id="#+id/camera_preview"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<LinearLayout
android:id="#+id/ticket_overlay"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<... Some stuff here ... w/ background>
<... spacer here ... w/ transparent to show view portal>
<... Some stuff here ... w/ background>
</LinearLayout>
</FrameLayout>
</RelativeLayout>
I'm having issues with the aspect ratio of a MediaRecorder in my Android app. I'm having the issue specifically with the Samsung Galaxy S II, whose video camera seems to zoom in compared to the regular camera (this is a behavior I notice when using the default camera app on the phone as well).
You can see how the aspect ratio is stretched when I switch from using the camera to using the MediaRecorder in this video:
https://www.youtube.com/watch?v=U8vCwiNjCPU
and in the screenshots below:
Camera aspect ratio (correct):
Video aspect ratio (incorrect):
How can I ensure that the aspect ratio of the video preview is correct?
Here is my code:
CustomCamera activity:
public class CustomCamera extends SherlockActivity {
private boolean prepareVideoRecorder() {
Log.d(TAG, "in prepareVideoRecorder()");
// It is very important to unlock the camera before doing setCamera
// or it will results in a black preview
if (camera == null)
{
camera = getCameraInstance();
}
if (recorder == null){
recorder = new MediaRecorder();
}
//Have to stop preview before starting to record
camera.stopPreview();
// Step 1: Unlock and set camera to MediaRecorder
camera.unlock();
recorder.setCamera(camera);
// Step 2: Set sources
recorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
recorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
// Step 3: Set a CamcorderProfile (requires API Level 8 or higher)
recorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH));
// Step 4: Set output file
recorder.setOutputFile(getOutputMediaFile(MEDIA_TYPE_VIDEO).getAbsolutePath());
// No limit. Don't forget to check the space on disk.
recorder.setMaxDuration(50000);
recorder.setVideoFrameRate(30);
recorder.setVideoEncodingBitRate(3000000);
recorder.setAudioEncodingBitRate(8000);
// Step 5: Set the preview output
recorder.setPreviewDisplay(cameraPreview.getHolder().getSurface());
//Setting the camera's orientation
int degree = 0;
// do not rotate image, just put rotation info in
switch (mOrientation) {
case ORIENTATION_LANDSCAPE_INVERTED:
degree = 180;
break;
case ORIENTATION_PORTRAIT_NORMAL:
degree = 90;
break;
case ORIENTATION_LANDSCAPE_NORMAL:
degree = 0;
break;
case ORIENTATION_PORTRAIT_INVERTED:
degree = 270;
break;
}
recorder.setOrientationHint(degree);
// Step 6: Prepare configured MediaRecorder
try {
recorder.prepare();
} catch (IllegalStateException e) {
// This is thrown if the previous calls are not called with the
// proper order
e.printStackTrace();
releaseMediaRecorder();
return false;
} catch (IOException e) {
releaseMediaRecorder();
e.printStackTrace();
return false;
}
//Everything went successfully
return true;
}
}
/**
* Method used to set the camera preview's parameters to match the
* phone's width and set the height accordingly to assure that there are
* no aspect ratio issues.
*/
private void setHolderParameters() {
Log.d(TAG, "setting camera layout parameters");
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
int height = metrics.heightPixels;
int width = metrics.widthPixels;
Size mPreviewSize = CameraPreview.getOptimalPreviewSize(camera.getParameters().getSupportedPreviewSizes(), width, height);
double ratio = ((double)mPreviewSize.width)/mPreviewSize.height;
FrameLayout.LayoutParams previewParams = new FrameLayout.LayoutParams(width, (int)(width*ratio));
cameraPreview.setLayoutParams(previewParams);
}
/**
* Open the camera asynchronously to reduce the lag when opening
* activity
*/
public void openCameraAsync(){
new AsyncTask<Object, Object, Object>(){
#Override
protected Object doInBackground(Object... arg0) {
if (!isFinishing()){
//Resuming camera and display when resuming
if(camera == null){
Log.d(TAG, "Resuming with a null camera");
camera = getCameraInstance();
}
}
return null;
}
#Override
protected void onPostExecute(Object result){
setHolderParameters();
cameraPreview.setCamera(camera);
//Calling surface created so that the preview of the camera is correct
cameraPreview.surfaceCreated(cameraPreview.getHolder());
}
}.execute();
}
CameraPreview.java:
public static Size getOptimalPreviewSize(List<Size> sizes, int w, int h) {
Log.d(TAG, "getOptimalPreviewSize");
final double ASPECT_TOLERANCE = 0.05;
double targetRatio = (double) w/h;
if (sizes==null) return null;
Size optimalSize = null;
double minDiff = Double.MAX_VALUE;
int targetHeight = h;
// Find 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);
}
}
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.d(TAG, "targetRatio: " + targetRatio);
Log.d(TAG, "optimalSize: " + optimalSize);
return optimalSize;
}
public void setRecorder(MediaRecorder recorder){
this.recorder = recorder;
}
Are setting any Camera.Parameters? You need to use setPreviewSize(int width, int height) and set it to the width and height of your video.
In your MediaRecorder, you may also need to use setVideoSize(int,int) and (again) set to the size of your video.
I was having the same issue you are having and to get the correct aspect ratio for your video, the layout size, Camera Preview size, and MediaRecorder size should have the same aspect ratio. Errors usually occur when one of these is off.
On some phones on measure returns different width and height than DisplayMatrics provide and that is why you get shrink-ed or widened picture when you press record.
specifically these values :
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
measuredWidth = resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec);
measuredHeight = resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec);
setMeasuredDimension(measuredWidth, measuredHeight);
//setMeasuredDimension(mPreviewSize.height, mPreviewSize.width);
}
}
from your surface view may be different than:
public static Camera.Size getDeviceSpecificOptimalPreviewSize(Context context, Camera camera, int w, int h) {
List<Camera.Size> mSupportedPreviewSizes = camera.getParameters().getSupportedPreviewSizes();
if (mSupportedPreviewSizes != null) {
final double ASPECT_TOLERANCE = 0.1;
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
int width = metrics.widthPixels;
int height = metrics.heightPixels;
double targetRatio = (double) height / width;
Camera.Size optimalSize = null;
double minDiff = Double.MAX_VALUE;
for (Camera.Size size : mSupportedPreviewSizes) {
double ratio = (double) size.width / size.height;
if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue;
if (Math.abs(size.height - h) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.height - h);
}
}
if (optimalSize == null) {
minDiff = Double.MAX_VALUE;
for (Camera.Size size : mSupportedPreviewSizes) {
if (Math.abs(size.height - h) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.height - h);
}
}
}
return optimalSize;
}
these values that return Camera.Size object. mPreviewSize in my case. You also need to set them on camera object in onSurfaceCreated and in onSurfaceChanged method in your SurfaceView class.
Be careful when setting the values because onMeasure on some phones returns height and width in reverse order.
I am on a Camera App which taking basic pictures. I have an issue when I get the best optimal preview size.
In fact, with this first code :
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
if (isPreviewRunning) {
mCamera.stopPreview();
}
Camera.Parameters parameters = mCamera.getParameters();
mCamera.setParameters(parameters);
mCamera.startPreview();
isPreviewRunning = true;
}
The picture has a good quality :
http://img689.imageshack.us/i/04042011172937.jpg/
But, with this code :
private Size getOptimalPreviewSize(List<Size> sizes, int w, int h) {
final double ASPECT_TOLERANCE = 0.05;
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;
}
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
// Now that the size is known, set up the camera parameters and begin
// the preview.
if (isPreviewRunning) {
mCamera.stopPreview();
}
Camera.Parameters parameters = mCamera.getParameters();
List<Size> sizes = parameters.getSupportedPreviewSizes();
Size optimalSize = getOptimalPreviewSize(sizes, w, h);
parameters.setPreviewSize(optimalSize.width, optimalSize.height);
mCamera.setParameters(parameters);
mCamera.startPreview();
isPreviewRunning =true;
}
The picture is totally distorted :
http://img97.imageshack.us/i/04042011173220.jpg/
How can I resolve this problem??
I am using a HTC Desire HD.
It is probably my saving method too ? :
PictureCallback mPictureCallbackJpeg = new PictureCallback() {
public void onPictureTaken(byte[] imageData, Camera camera) {
....
BitmapFactory.Options options=new BitmapFactory.Options();
options.inSampleSize = 5;
Bitmap myImage = BitmapFactory.decodeByteArray(imageData, 0,imageData.length,options);
FileOutputStream fOut = new FileOutputStream(path + "/"+fl);
BufferedOutputStream bos = new BufferedOutputStream(fOut);
myImage.compress(CompressFormat.JPEG, 100, bos);
bos.flush();
bos.close();
....
}
Thanks
I just suffered through the same problem, and came up with a similar but lighter weight solution. I don't see anything wrong with your saving method though.
private Camera.Size getBestPreviewSize(int width, int height)
{
Camera.Size result=null;
Camera.Parameters p = camera.getParameters();
for (Camera.Size size : p.getSupportedPreviewSizes()) {
if (size.width<=width && size.height<=height) {
if (result==null) {
result=size;
} else {
int resultArea=result.width*result.height;
int newArea=size.width*size.height;
if (newArea>resultArea) {
result=size;
}
}
}
}
return result;
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
//This line helped me set the preview Display Orientation to Portrait
//Only works API Level 8 and higher unfortunately.
camera.setDisplayOrientation(90);
Camera.Parameters parameters = camera.getParameters();
Camera.Size size = getBestPreviewSize(width, height);
parameters.setPreviewSize(size.width, size.height);
camera.setParameters(parameters);
camera.startPreview();
}
The best way is to get preview size with the closest aspect ratio:
Camera.Size getBestPreviewSize(int width, int height, Camera.Parameters parameters) {
Camera.Size result=null;
float dr = Float.MAX_VALUE;
float ratio = (float)width/(float)height;
for (Camera.Size size : parameters.getSupportedPreviewSizes()) {
float r = (float)size.width/(float)size.height;
if( Math.abs(r - ratio) < dr && size.width <= width && size.height <= height ) {
dr = Math.abs(r - ratio);
result = size;
}
}
return result;
}
Where width and height parameters are your surface size.