I am trying to create custom camera preview logic that actually works properly, for all scenarios:
any device: phone, tablet
any camera: front-facing, rear-facing
android.hardware.Camera
My android:minSdkVersion is 14 and android:targetSdkVersion is 21.
I have implemented custom camera preview class to set display orientation for camera preview and working very well for all devices without only Nexus devices. Nexus devices have I think default 180 orientation.
While I am starting my camera in Nexus devices it is showing inverted. To overcome with i have checked with Build.MANUFACTURER & Build.MODEL to identify the device and set orientation according to it.
if (Build.MODEL.equals("Nexus 6P") && Build.MANUFACTURER.equals("Huawei")) mCamera.setDisplayOrientation(90);
else mCamera.setDisplayOrientation(270);
But it is not working. So can anyone have idea to overcome with this and recommended way !! Advance help would be appreciated !!
As far as I know this problem doesn't occur on Nexus 5. I had to deal with it on a Nexus 5X and I lost some time trying(Click here to see why). As a big note, I can't guarantee that it's the best solution, but it fixed all the problems I had. To solve it I did something like this:
I created a class CameraPreview extends SurfaceView only to encapsulate all the preview initialisation in one place. Here is the constructor for that class:
public CameraPreview(Context context, int screenRotation, Camera camera) {
super(context);
mCamera = camera;
mScreenRotation = screenRotation;
mHolder = getHolder();
mHolder.addCallback(this);
setFocusable(true);
setFocusableInTouchMode(true);
}
To create the preview I used this: mPreview = new CameraPreview(getContext(), screenRotation, mCamera);, where screenRotation is int screenRotation = getActivity().getWindowManager().getDefaultDisplay().getRotation();
When the surfaceCreated callback is called:
public void surfaceCreated(SurfaceHolder holder) {
try {
mCamera.setPreviewDisplay(holder);
setCameraDisplayOrientation(mScreenRotation, 0, mCamera);
Here,0 comes from the cameraId (it might be different if you're using the front facing camera). And here is all the magic:
public void setCameraDisplayOrientation(int screenRotation, int cameraId, Camera camera) {
int rotation = getRotationAngle(screenRotation, cameraId);
camera.setDisplayOrientation(rotation);
}
public static int getRotationAngle(int screenRotation, int cameraId) {
Camera.CameraInfo info = new Camera.CameraInfo();
android.hardware.Camera.getCameraInfo(cameraId, info);
int degrees = 0;
switch (screenRotation) {
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;
}
return result;
}
I hope that this will fix the problem, but before starting to write it, make sure that you read the link I provided to see why the problem takes place. Hope this will fix it for you too.
To initiate in activity :
CameraPreviewNew mPreview = new ResizableCameraPreview(this, cameraId, CameraPreviewNew.LayoutMode.NoBlank, false, screenHeight, screenWidth); // cameraId for front or rear
LinearLayout.LayoutParams previewLayoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
frameCamera.addView(mPreview, 0, previewLayoutParams);
CameraPreviewNew.java
public class CameraPreviewNew extends SurfaceView implements SurfaceHolder.Callback {
private static boolean DEBUGGING = false;
private static final String LOG_TAG = "CameraPreviewSample";
private static final String CAMERA_PARAM_ORIENTATION = "orientation";
private static final String CAMERA_PARAM_LANDSCAPE = "landscape";
private static final String CAMERA_PARAM_PORTRAIT = "portrait";
protected Activity mActivity;
private SurfaceHolder mHolder;
protected Camera mCamera;
protected List<Camera.Size> mPreviewSizeList;
protected List<Camera.Size> mPictureSizeList;
protected Camera.Size mPreviewSize;
protected Camera.Size mPictureSize;
private int mSurfaceChangedCallDepth = 0;
private int mCameraId;
private LayoutMode mLayoutMode;
private int mCenterPosX = -1;
private int mCenterPosY;
private int screenHeight, screenWidth;
PreviewReadyCallback mPreviewReadyCallback = null;
public enum LayoutMode {
FitToParent, // Scale to the size that no side is larger than the parent
NoBlank // Scale to the size that no side is smaller than the parent
}
public interface PreviewReadyCallback {
void onPreviewReady();
}
/**
* State flag: true when surface's layout size is set and surfaceChanged()
* process has not been completed.
*/
protected boolean mSurfaceConfiguring = false;
public CameraPreviewNew(Activity activity, int cameraId, LayoutMode mode, int screenHeight, int screenWidth) {
super(activity); // Always necessary
mActivity = activity;
mLayoutMode = mode;
this.screenHeight = screenHeight;
this.screenWidth = screenWidth;
mHolder = getHolder();
mHolder.addCallback(this);
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
// mHolder.setFixedSize(fixWidth, fixHeight);
// FileLog.v("Camera ID ::::::::::: " + cameraId);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
if (Camera.getNumberOfCameras() > cameraId) {
mCameraId = cameraId;
} else {
mCameraId = 0;
}
} else {
mCameraId = 0;
}
// FileLog.d("Camera ID ::::::::::: " + cameraId);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
mCamera = Camera.open(mCameraId);
} else {
mCamera = Camera.open();
}
Camera.Parameters cameraParams = mCamera.getParameters();
mPreviewSizeList = cameraParams.getSupportedPreviewSizes();
mPictureSizeList = cameraParams.getSupportedPictureSizes();
// FileLog.d("Preview Size ID ::::::::::: " + mPreviewSizeList);
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
try {
mCamera.setPreviewDisplay(mHolder);
} catch (IOException e) {
mCamera.release();
mCamera = null;
}
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
mSurfaceChangedCallDepth++;
doSurfaceChanged(width, height);
mSurfaceChangedCallDepth--;
}
public void doSurfaceChanged(int width, int height) {
mCamera.stopPreview();
Camera.Parameters cameraParams = mCamera.getParameters();
boolean portrait = isPortrait();
// The code in this if-statement is prevented from executed again when surfaceChanged is
// called again due to the change of the layout size in this if-statement.
if (!mSurfaceConfiguring) {
Camera.Size previewSize = determinePreviewSize(portrait, width, height);
Camera.Size pictureSize = determinePictureSize(previewSize);
if (DEBUGGING) { Log.v(LOG_TAG, "Desired Preview Size - w: " + width + ", h: " + height); }
mPreviewSize = previewSize;
mPictureSize = pictureSize;
mSurfaceConfiguring = adjustSurfaceLayoutSize(previewSize, portrait, width, height);
// Continue executing this method if this method is called recursively.
// Recursive call of surfaceChanged is very special case, which is a path from
// the catch clause at the end of this method.
// The later part of this method should be executed as well in the recursive
// invocation of this method, because the layout change made in this recursive
// call will not trigger another invocation of this method.
if (mSurfaceConfiguring && (mSurfaceChangedCallDepth <= 1)) {
return;
}
}
configureCameraParameters(cameraParams, portrait);
mSurfaceConfiguring = false;
try {
mCamera.startPreview();
} catch (Exception e) {
Log.w(LOG_TAG, "Failed to start preview: " + e.getMessage());
// Remove failed size
mPreviewSizeList.remove(mPreviewSize);
mPreviewSize = null;
// Reconfigure
if (mPreviewSizeList.size() > 0) { // prevent infinite loop
surfaceChanged(null, 0, width, height);
} else {
Log.w(LOG_TAG, "Gave up starting preview");
}
}
if (null != mPreviewReadyCallback) {
mPreviewReadyCallback.onPreviewReady();
}
}
/**
* #param portrait
* #param reqWidth must be the value of the parameter passed in surfaceChanged
* #param reqHeight must be the value of the parameter passed in surfaceChanged
* #return Camera.Size object that is an element of the list returned from Camera.Parameters.getSupportedPreviewSizes.
*/
protected Camera.Size determinePreviewSize(boolean portrait, int reqWidth, int reqHeight) {
// Meaning of width and height is switched for preview when portrait,
// while it is the same as user's view for surface and metrics.
// That is, width must always be larger than height for setPreviewSize.
int reqPreviewWidth; // requested width in terms of camera hardware
int reqPreviewHeight; // requested height in terms of camera hardware
if (portrait) {
reqPreviewWidth = reqHeight;
reqPreviewHeight = reqWidth;
} else {
reqPreviewWidth = reqWidth;
reqPreviewHeight = reqHeight;
}
if (DEBUGGING) {
Log.v(LOG_TAG, "Listing all supported preview sizes");
for (Camera.Size size : mPreviewSizeList) {
Log.v(LOG_TAG, " w: " + size.width + ", h: " + size.height);
}
Log.v(LOG_TAG, "Listing all supported picture sizes");
for (Camera.Size size : mPictureSizeList) {
Log.v(LOG_TAG, " w: " + size.width + ", h: " + size.height);
}
}
// Adjust surface size with the closest aspect-ratio
float reqRatio = ((float) reqPreviewWidth) / reqPreviewHeight;
float curRatio, deltaRatio;
float deltaRatioMin = Float.MAX_VALUE;
Camera.Size retSize = null;
for (Camera.Size size : mPreviewSizeList) {
curRatio = ((float) size.width) / size.height;
deltaRatio = Math.abs(reqRatio - curRatio);
if (deltaRatio < deltaRatioMin) {
deltaRatioMin = deltaRatio;
retSize = size;
}
}
retSize = mPreviewSizeList.get(0);
return retSize;
}
protected Camera.Size determinePictureSize(Camera.Size previewSize) {
Camera.Size retSize = null;
for (Camera.Size size : mPictureSizeList) {
if (size.equals(previewSize)) {
return size;
}
}
if (DEBUGGING) { Log.v(LOG_TAG, "Same picture size not found."); }
// if the preview size is not supported as a picture size
float reqRatio = ((float) previewSize.width) / previewSize.height;
float curRatio, deltaRatio;
float deltaRatioMin = Float.MAX_VALUE;
for (Camera.Size size : mPictureSizeList) {
curRatio = ((float) size.width) / size.height;
deltaRatio = Math.abs(reqRatio - curRatio);
if (deltaRatio < deltaRatioMin) {
deltaRatioMin = deltaRatio;
retSize = size;
}
}
retSize = mPictureSizeList.get(0);
return retSize;
}
protected boolean adjustSurfaceLayoutSize(Camera.Size previewSize, boolean portrait,
int availableWidth, int availableHeight) {
float tmpLayoutHeight, tmpLayoutWidth;
if (portrait) {
tmpLayoutHeight = previewSize.width;
tmpLayoutWidth = previewSize.height;
} else {
tmpLayoutHeight = previewSize.height;
tmpLayoutWidth = previewSize.width;
}
float factH, factW, fact;
factH = availableHeight / tmpLayoutHeight;
factW = availableWidth / tmpLayoutWidth;
if (mLayoutMode == LayoutMode.FitToParent) {
// Select smaller factor, because the surface cannot be set to the size larger than display metrics.
if (factH < factW) {
fact = factH;
} else {
fact = factW;
}
} else {
if (factH < factW) {
fact = factW;
} else {
fact = factH;
}
}
LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams)this.getLayoutParams();
int layoutHeight = (int) (tmpLayoutHeight * fact);
int layoutWidth = (int) (tmpLayoutWidth * fact);
if (DEBUGGING) {
Log.v(LOG_TAG, "Preview Layout Size - w: " + layoutWidth + ", h: " + layoutHeight);
Log.v(LOG_TAG, "Scale factor: " + fact);
}
boolean layoutChanged;
if ((layoutWidth != this.getWidth()) || (layoutHeight != this.getHeight())) {
int diffHeight = (screenHeight - layoutHeight) / 2;
layoutParams.height = layoutHeight + diffHeight;
int diffWidth = (screenWidth - layoutWidth) / 2;
layoutParams.width = layoutWidth + diffWidth;
if (mCenterPosX >= 0) {
layoutParams.topMargin = mCenterPosY - (layoutHeight / 2);
layoutParams.leftMargin = mCenterPosX - (layoutWidth / 2);
}
this.setLayoutParams(layoutParams); // this will trigger another surfaceChanged invocation.
layoutChanged = true;
} else {
layoutChanged = false;
}
return layoutChanged;
}
/**
* #param x X coordinate of center position on the screen. Set to negative value to unset.
* #param y Y coordinate of center position on the screen.
*/
public void setCenterPosition(int x, int y) {
mCenterPosX = x;
mCenterPosY = y;
}
protected void configureCameraParameters(Camera.Parameters cameraParams, boolean portrait) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.FROYO) { // for 2.1 and before
if (portrait) {
cameraParams.set(CAMERA_PARAM_ORIENTATION, CAMERA_PARAM_PORTRAIT);
} else {
cameraParams.set(CAMERA_PARAM_ORIENTATION, CAMERA_PARAM_LANDSCAPE);
}
} else { // for 2.2 and later
int angle;
Display display = mActivity.getWindowManager().getDefaultDisplay();
switch (display.getRotation()) {
case Surface.ROTATION_0: // This is display orientation
angle = 90; // This is camera orientation
break;
case Surface.ROTATION_90:
angle = 0;
break;
case Surface.ROTATION_180:
angle = 270;
break;
case Surface.ROTATION_270:
angle = 180;
break;
default:
angle = 90;
break;
}
Log.v(LOG_TAG, "angle: " + angle);
mCamera.setDisplayOrientation(angle);
}
cameraParams.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
cameraParams.setPictureSize(mPictureSize.width, mPictureSize.height);
cameraParams.setZoom(0);
// if (cameraParams.isZoomSupported()) {
final int maxZoomLevel = cameraParams.getMaxZoom();
Log.e("max ZOOM ", "is " + maxZoomLevel);
// }
// cameraParams.setPreviewSize(fixWidth, fixHeight);
// cameraParams.setPictureSize(fixWidth, fixHeight);
if (DEBUGGING) {
Log.v(LOG_TAG, "Preview Actual Size - w: " + mPreviewSize.width + ", h: " + mPreviewSize.height);
Log.v(LOG_TAG, "Picture Actual Size - w: " + mPictureSize.width + ", h: " + mPictureSize.height);
}
mCamera.setParameters(cameraParams);
}
#SuppressWarnings("unused")
private Size getOptimalPreviewSize(List<Size> sizes, int w, int h) {
final double ASPECT_TOLERANCE = 0.2;
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) {
Log.d("Camera", "Checking size " + size.width + "w " + size.height
+ "h");
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;
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
stop();
}
public void stop() {
try {
if (null == mCamera) {
return;
}
if(mCamera != null) {
mCamera.stopPreview();
mCamera.release();
mCamera = null;
}
}catch (Exception e){
e.printStackTrace();
}
}
public boolean isPortrait() {
return (mActivity.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT);
}
public void setOneShotPreviewCallback(PreviewCallback callback) {
if (null == mCamera) {
return;
}
mCamera.setOneShotPreviewCallback(callback);
}
public void setPreviewCallback(PreviewCallback callback) {
if (null == mCamera) {
return;
}
mCamera.setPreviewCallback(callback);
}
public Camera.Size getPreviewSize() {
return mPreviewSize;
}
public void setOnPreviewReady(PreviewReadyCallback cb) {
mPreviewReadyCallback = cb;
}
public Camera getPreviewCamera() {
return mCamera;
}
}
ResizableCameraPreview.java
public class ResizableCameraPreview extends CameraPreviewNew {
private static boolean DEBUGGING = false;
private static final String LOG_TAG = "ResizableCameraPreviewSample";
/**
* #param activity
* #param addReversedSizes is set to true to add reversed values of supported preview-sizes to the list.
*/
public ResizableCameraPreview(Activity activity, int cameraId, LayoutMode mode, boolean addReversedSizes, int screenHeight, int screenWidth) {
super(activity, cameraId, mode, screenHeight, screenWidth);
if (addReversedSizes) {
List<Camera.Size> sizes = mPreviewSizeList;
int length = sizes.size();
for (int i = 0; i < length; i++) {
Camera.Size size = sizes.get(i);
Camera.Size revSize = mCamera.new Size(size.height, size.width);
sizes.add(revSize);
}
}
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
mCamera.stopPreview();
Camera.Parameters cameraParams = mCamera.getParameters();
boolean portrait = isPortrait();
if (!mSurfaceConfiguring) {
Camera.Size previewSize = determinePreviewSize(portrait, width, height);
Camera.Size pictureSize = determinePictureSize(previewSize);
Log.v(LOG_TAG, "Desired Preview Size - w: " + width + ", h: " + height);
mPreviewSize = previewSize;
mPictureSize = pictureSize;
mSurfaceConfiguring = adjustSurfaceLayoutSize(previewSize, portrait, width, height);
if (mSurfaceConfiguring) {
return;
}
}
configureCameraParameters(cameraParams, portrait);
mSurfaceConfiguring = false;
try {
mCamera.startPreview();
} catch (Exception e) {
Log.w(LOG_TAG, "Failed to start preview: " + e.getMessage());
}
}
/**
*
* #param index selects preview size from the list returned by CameraPreview.getSupportedPreivewSizes().
* #param width is the width of the available area for this view
* #param height is the height of the available area for this view
*/
public void setPreviewSize(int index, int width, int height) {
mCamera.stopPreview();
Camera.Parameters cameraParams = mCamera.getParameters();
boolean portrait = isPortrait();
Camera.Size previewSize = mPreviewSizeList.get(index);
Camera.Size pictureSize = determinePictureSize(previewSize);
if (DEBUGGING) { Log.v(LOG_TAG, "Requested Preview Size - w: " + previewSize.width + ", h: " + previewSize.height); }
mPreviewSize = previewSize;
mPictureSize = pictureSize;
boolean layoutChanged = adjustSurfaceLayoutSize(previewSize, portrait, width, height);
if (layoutChanged) {
mSurfaceConfiguring = true;
return;
}
configureCameraParameters(cameraParams, portrait);
try {
mCamera.startPreview();
} catch (Exception e) {
}
mSurfaceConfiguring = false;
}
public List<Camera.Size> getSupportedPreivewSizes() {
return mPreviewSizeList;
}
}
Xml file :
<FrameLayout
android:id="#+id/frm"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<LinearLayout
android:id="#+id/frameCamera"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
</LinearLayout>
</FrameLayout>
Related
I am using Android Camera API and it works good 16:9 ratio devices on both portrait and landscape modes. But in Samsung S9 18:9 ratio device, on landscape mode the preview looks stretched. On Samsung S9 I got following supported preview sizes, 1920X1080,1440X1080, 1088X1088,1280X720,1056X704, 1024X768, 960X720,800X450,720X720,720X480,640X480,352X288,320X240,256X144,176X144 So optimal preview size is 1920X1080 but actual resolution of device is 2,220 x 1,080. So that it looks stretched.But I need preview on full screen.How default camera preview shows on fullscreen?
#SuppressLint("ClickableViewAccessibility")
#SuppressWarnings("deprecation")
public CameraPreview(Context context, Camera.PreviewCallback previewCallback) {
super(context);
this.previewCallback = previewCallback;
mContext = context;
// Install a SurfaceHolder.Callback so we get notified when the
// underlying surface is created and destroyed.
mHolder = getHolder();
mHolder.addCallback(this);
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
public void setCameraDisplayOrientation(Context activity,
int cameraId, Camera camera) {
Camera.CameraInfo info =
new Camera.CameraInfo();
Camera.getCameraInfo(cameraId, info);
int rotation = ((AppCompatActivity) 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;
}
mDisplayOrientation = result;
Log.d(TAG, "setCameraDisplayOrientation: "+mDisplayOrientation);
camera.setDisplayOrientation(result);
}
public void takePhoto(final PictureCallback pCalback) {
mCamera.takePicture(null, null, pCalback);
}
public void surfaceCreated(SurfaceHolder holder) {
// The Surface has been created, acquire the camera and tell it where
// to draw.
if (mCamera != null) {
mCamera.stopPreview();
mCamera.setPreviewCallback(null);
mCamera.release();
mCamera = null;
//previewCount = 0;
}
try {
mCamera = Camera.open();
//setCameraDisplayOrientation(mContext, 0, mCamera);
/*mParameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
DisplayMetrics displayMetrics = mContext.getResources().getDisplayMetrics();
int screenWidth = displayMetrics.widthPixels;
int screenHeight = displayMetrics.heightPixels;
optimalPreviewSize = getBestAspectPreviewSize(mParameters.getSupportedPreviewSizes(), screenWidth, screenHeight);//Bug Fix for Samsung A8
mParameters.setPreviewSize(optimalPreviewSize.width, optimalPreviewSize.height);
mParameters.setPictureSize(optimalPreviewSize.width, optimalPreviewSize.height);
mParameters.setPreviewFpsRange(30000, 30000);
mCamera.setParameters(mParameters);*/
/*mCamera.setPreviewDisplay(holder);
mCamera.setPreviewCallback(previewCallback);*/
mCamera.setPreviewDisplay(holder);
} catch (IOException exception) {
mCamera.release();
mCamera = null;
//previewCount = 0;
} catch (Exception exception) {
mCamera = null;
//previewCount = 0;
}
if (mCameraPreviewListener != null) {
mCameraPreviewListener.onCameraSurfaceCreated();
}
}
public void surfaceDestroyed(SurfaceHolder holder) {
if (mCamera != null) {
mCamera.stopPreview();
mCamera.setPreviewCallback(null);
mCamera.release();
mCamera = null;
//previewCount = 0;
}
if (mCameraPreviewListener != null) {
mCameraPreviewListener.onCameraSurfaceDestroyed();
}
}
public void stopCamera() {
if (mCamera != null) {
mCamera.stopPreview();
mCamera.setPreviewCallback(null);
mCamera.release();
mCamera = null;
//previewCount = 0;
}
}
#SuppressWarnings("null")
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
try {
// Now that the size is known, set up the camera parameters and begin
// the preview.
mParameters = mCamera.getParameters();
Log.d("CameraFix", "parameters -> " + mParameters.flatten());
setCameraDisplayOrientation(mContext, 0, mCamera);
mParameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
//Size optimalPreviewSize = getOptimalPreviewSize(mParameters.getSupportedPreviewSizes(), getWidth(), getHeight());
DisplayMetrics displayMetrics = mContext.getResources().getDisplayMetrics();
int screenWidth = displayMetrics.widthPixels;
int screenHeight = displayMetrics.heightPixels;
//Size optimalPreviewSize = getOptimalPreviewSize(mParameters.getSupportedPreviewSizes(), screenWidth, screenHeight, getHeight());
mSupportedPreviewSizes = mParameters.getSupportedPreviewSizes();
optimalPreviewSize = getBestAspectPreviewSize(mParameters.getSupportedPreviewSizes(), screenWidth, screenHeight);//Bug Fix for Samsung A8
Log.d("CameraFix", "optimalPreviewSize.width -> " + optimalPreviewSize.width);
Log.d("CameraFix", "optimalPreviewSize.height -> " + optimalPreviewSize.height);
mParameters.setPreviewSize(optimalPreviewSize.width, optimalPreviewSize.height);
mParameters.setPictureSize(optimalPreviewSize.width, optimalPreviewSize.height);
mParameters.setPreviewFpsRange(30000, 30000);
/*if (mDisplayOrientation == 0 || mDisplayOrientation == 180) {
setLayoutParams(new FrameLayout.LayoutParams(optimalPreviewSize.width, optimalPreviewSize.height,Gravity.CENTER));
}*/
Log.d("CameraFix", "setPreviewFpsRange");
mCamera.setParameters(mParameters);
mCamera.setPreviewDisplay(holder);
//SurfaceTexture st = new SurfaceTexture(10);
//mCamera.setPreviewTexture(st);
mCamera.setPreviewCallback(previewCallback);
mCamera.startPreview();
Log.d("CameraFix", "start preview");
if (mCameraPreviewListener != null) {
mCameraPreviewListener.onCameraSurfaceChanged();
}
} catch (Exception e) {
e.printStackTrace();
Log.d("CameraFix", e.toString());
}
}
public void toggleFlash(boolean flashModeOn) {
if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_FLASH)) {
Parameters parameters = mCamera.getParameters();
if (flashModeOn) {
//parameters.setFlashMode(Parameters.FLASH_MODE_TORCH);
parameters.setFlashMode(Parameters.FLASH_MODE_ON);
mCamera.setParameters(parameters);
mCamera.startPreview();
//Toast.makeText(mContext, R.string.flash_mode_on, Toast.LENGTH_SHORT).show();
} else {
parameters.setFlashMode(Parameters.FLASH_MODE_OFF);
mCamera.setParameters(parameters);
//Toast.makeText(mContext, R.string.flash_mode_off, Toast.LENGTH_SHORT).show();
}
} else {
Toast.makeText(mContext, R.string.flash_not_available, Toast.LENGTH_SHORT).show();
}
}
/**
* Source for this solution - https://stackoverflow.com/questions/21354313/camera-preview-quality-in-android-is-poor/21354442#21354442
*
* #param supportedPreviewSizes
* #param screenWidth
* #param screenHeight
* #return
*/
private Size getBestAspectPreviewSize(List<Size> supportedPreviewSizes, int screenWidth, int screenHeight) {
double targetRatio = (double) screenWidth / screenHeight;
Camera.Size optimalSize = null;
double minDiff = Double.MAX_VALUE;
for (int i = 0; i < supportedPreviewSizes.size(); i++) {
Size size = supportedPreviewSizes.get(i);
Log.d(TAG, "getBestAspectPreviewSize: supportedPreviewSizes -> "+size.width +"X"+size.height);
}
Log.d(TAG, "getBestAspectPreviewSize: supportedPreviewSizes -> "+supportedPreviewSizes.toString());
Log.d(TAG, "getBestAspectPreviewSize: mDisplayOrientation -> "+mDisplayOrientation);
if (mDisplayOrientation == 90 || mDisplayOrientation == 270) {
Log.d(TAG, "getBestAspectPreviewSize: inside 90 - 270 ");
targetRatio = (double) screenHeight / screenWidth;
}
Log.d(TAG, "getBestAspectPreviewSize: targetRatio -> "+targetRatio);
Collections.sort(supportedPreviewSizes,
Collections.reverseOrder(new SizeComparator()));
for (Size size : supportedPreviewSizes) {
double ratio = (double) size.width / size.height;
if (Math.abs(ratio - targetRatio) < minDiff) {
optimalSize = size;
minDiff = Math.abs(ratio - targetRatio);
}
if (minDiff < 0.0d) {
break;
}
}
return (optimalSize);
/*if (mDisplayOrientation == 0 || mDisplayOrientation == 180) {
if (optimalSize != null) {
return mCamera.new Size(optimalSize.height, optimalSize.width);
} else {
return null;
}
}
else{
return (optimalSize);
}*/
//return mCamera.new Size(2220,1080);
}
public int getDisplayOrientation() {
return mDisplayOrientation;
}
public void setDisplayOrientation(int displayOrientation) {
this.mDisplayOrientation = displayOrientation;
}
public Parameters getCameraParameters() {
return mCamera.getParameters();
}
public void setCameraPreviewListener(CameraPreviewListener cameraPreviewListener) {
mCameraPreviewListener = cameraPreviewListener;
}
public interface CameraPreviewListener {
void onCameraSurfaceCreated();
void onCameraSurfaceChanged();
void onCameraSurfaceDestroyed();
void onCameraPreviewStarted();
}
private static class SizeComparator implements
Comparator<Size> {
#Override
public int compare(Size lhs, Size rhs) {
int left = lhs.width * lhs.height;
int right = rhs.width * rhs.height;
if (left < right) {
return (-1);
} else if (left > right) {
return (1);
}
return (0);
}
}
You should check the displayable area of default camera again. I don't think it can display fully without stretch in that resolution. It may have a black area, toolbar, status bar...
There is nothing wrong in your implementation. We have to find the best support preview size compare with surface view which you want to display. In this case, you should make the surface view (1920 x 1080) in center, then add black padding areas on top and bottom.
Please take look into my answer hope it will help, I resolved the stretch issue by the following code, method name might change. I share my implementation, Because I know how hard to implement camera in Android, So please don't hesitate to see the following section.
Invoke loadCamera method in ButtonAction.
private void loadCamera() {
if (CommonUtils.deviceHasCamera(getActivityContext)) {
startBackgroundThread();
mCameraTimeOut=(isPermissionGranted?2500:5000);
if (mTextureView.isAvailable()) {
openCamera(mTextureView.getWidth(), mTextureView.getHeight());
} else {
mTextureView.setSurfaceTextureListener(mSurfaceTextureListener);
}
}else{
ShowToastUtils.INSTANCE.showCustomToast(getActivityContext, getString(R.string.msg_no_camera));
}
}
Initially SurfaceListener Invoked for Camera
private TextureView.SurfaceTextureListener mSurfaceTextureListener
= new TextureView.SurfaceTextureListener() {
#Override
public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture,
int width, int height) {
mCameraTimeOut=(isPermissionGranted?2500:5000);
Log.e(TAG1, "chooseOptimalSize"+"-SurfaceTextureListener ---=>Width---=>"+width);
Log.e(TAG1, "chooseOptimalSize"+"-SurfaceTextureListener ---=>Height---=>"+height);
openCamera(width, height);
}
#Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture,
int width, int height) {
configureTransform(width, height);
}
#Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) {
return true;
}
#Override
public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) {}
};
To Choose Optimal Preview Size for Texture
//Samsung-S6-choices[0]
//Samsung-S7-edge-choices[6]
//OnePlus-5T-choices[15]
/*Following is used for Camera Preview in TextureView, based on device camera resolution*/
/*
* Given {#code choices} of {#code Size}s supported by a camera, chooses the smallest one whose
* width and height are at least as large as the respective requested values, and whose aspect
* ratio matches with the specified value.
*
* #param choices The list of sizes that the camera supports for the intended output class
* #param width The minimum desired width
* #param height The minimum desired height
* #param aspectRatio The aspect ratio
* #return The optimal {#code Size}, or an arbitrary one if none were big enough
*/
private Size chooseOptimalSize(Size[] choices, int width, int height, Size aspectRatio) {
// Collect the supported resolutions that are at least as big as the preview Surface
int loopCounter=0;
Log.e(TAG1, "Screen-->Width x Height="+screenWidth+" x "+screenHeight);
for (Size size : choices) {
Log.e(TAG1, "chooseOptimalSize:"+size);
}
for (Size size : choices) {
int orientation = getActivityContext.getResources().getConfiguration().orientation;
if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
if((size.getWidth()/16) == (size.getHeight()/9) && size.getWidth() <=7680 ) {//8K UHDTV Super Hi-Vision
Log.e(TAG1, "chooseOptimalSize:"+size.getWidth()+"x"+size.getHeight()+"--LoopPosition---==>"+loopCounter);
return size;
}
} else {
Log.e(TAG1, "chooseOptimalSize:--given--"+size);
if((size.getWidth()/16) == (size.getHeight()/9) && ((size.getWidth() <=1280)||(size.getHeight()<=1920))) {
mCameraRatio=RATIO_16_9;
Log.e(TAG1, "chooseOptimalSize:"+size.getWidth()+"x"+size.getHeight()+"-16:9"+"--LoopPosition---==>"+loopCounter);
return size;
}else if((size.getWidth()/18) == (size.getHeight()/9) && ((size.getWidth() <=2160)||(size.getHeight()<=3840))) {
mCameraRatio=RATIO_18_9;
Log.e(TAG1, "chooseOptimalSize:"+size.getWidth()+"x"+size.getHeight()+"-18:9"+"--LoopPosition---==>"+loopCounter);
return size;
}else if((size.getWidth()/18.5) == (size.getHeight()/9) && ((size.getWidth() <=2160)||(size.getHeight()<=3840))) {
mCameraRatio=RATIO_18_9;
Log.e(TAG1, "chooseOptimalSize:"+size.getWidth()+"x"+size.getHeight()+"-18.5:9"+"--LoopPosition---==>"+loopCounter);
return size;
}else if((width/19) == (height/9) && ((width <=2208)||(height<=3216))) {
mCameraRatio=RATIO_19_9;
Log.e(TAG1, "chooseOptimalSize:"+size.getWidth()+"x"+size.getHeight()+"-19:9"+"--LoopPosition---==>"+loopCounter);
return size;
}else if((size.getWidth()/19.5) == (size.getHeight()/9) && ((size.getWidth() <=3840)||(size.getHeight()<=2160))) {
mCameraRatio=RATIO_19_9;
Log.e(TAG1, "chooseOptimalSize:"+size.getWidth()+"x"+size.getHeight()+"-19.5:9"+"--LoopPosition---==>"+loopCounter);
return size;
}else{
Log.e(TAG1, "chooseOptimalSize"+" not proper aspect resolution");
}
}
loopCounter++;
}
}
To Open Camera
private void openCamera(int width, int height) {
CameraManager manager = (CameraManager) getActivityContext.getSystemService(Context.CAMERA_SERVICE);
try {
Log.e(TAG, "tryAcquire");
if (!mCameraOpenCloseLock.tryAcquire(mCameraTimeOut, TimeUnit.MILLISECONDS)) {
throw new RuntimeException("Time out waiting to lock camera opening.");
}
String mCameraId = manager.getCameraIdList()[cameraId];
// Choose the sizes for camera preview and video recording
characteristics = manager.getCameraCharacteristics(mCameraId);
StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
try {
mSensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
maximumZoomLevel = characteristics.get(CameraCharacteristics.SCALER_AVAILABLE_MAX_DIGITAL_ZOOM);
if (map == null) {
throw new RuntimeException("Cannot get available preview/video sizes");
}
mVideoSize = chooseVideoSize(map.getOutputSizes(MediaRecorder.class));
/*This Line will configure the Texture size*/
mPreviewSize = chooseOptimalSize(map.getOutputSizes(SurfaceTexture.class), width, height, mVideoSize);
int orientation = getResources().getConfiguration().orientation;
if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
Log.e(TAG1, "Width" + mPreviewSize.getWidth() + "X Height" + mPreviewSize.getHeight());
mTextureView.setAspectRatio(mPreviewSize.getWidth(), mPreviewSize.getHeight());
} else {
Log.e(TAG1, "Width" + mPreviewSize.getHeight() + "X Height" + mPreviewSize.getWidth());
mTextureView.setAspectRatio(mPreviewSize.getHeight(), mPreviewSize.getWidth());
//S10 preview Size
/* mTextureView.setAspectRatio(1080, 2280);*/
//mTextureView.setAspectRatio(2208, 2944);
}
if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
configureTransform(width, height);
}
if (isPermissionGranted) {
manager.openCamera(mCameraId, mStateCallback, null);
}
}catch (Exception ex){ex.printStackTrace();}finally {
map=null;
Runtime.getRuntime().gc();
}
} catch (CameraAccessException e) {
Toast.makeText(getActivityContext, "Cannot access the camera.", Toast.LENGTH_SHORT).show();
//getActivityContext.finish();
e.printStackTrace();
} catch (NullPointerException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
throw new RuntimeException("Interrupted while trying to lock camera opening.");
}
}
ConfigureTransform method used for orientation handling
/*
* Configures the necessary {#link android.graphics.Matrix} transformation to `mTextureView`.
* This method should not to be called until the camera preview size is determined in
* openCamera, or until the size of `mTextureView` is fixed.
*
* #param viewWidth The width of `mTextureView`
* #param viewHeight The height of `mTextureView`
*/
private void configureTransform(int viewWidth, int viewHeight) {
if (null == mTextureView || null == mPreviewSize) {
return;
}
int rotation = getActivityContext.getWindowManager().getDefaultDisplay().getRotation();
Matrix matrix = new Matrix();
RectF viewRect = new RectF(0, 0, viewWidth, viewHeight);
RectF bufferRect = new RectF(0, 0, mPreviewSize.getHeight(), mPreviewSize.getWidth());
float centerX = viewRect.centerX();
float centerY = viewRect.centerY();
if (Surface.ROTATION_90 == rotation || Surface.ROTATION_270 == rotation) {
bufferRect.offset(centerX - bufferRect.centerX(), centerY - bufferRect.centerY());
matrix.setRectToRect(viewRect, bufferRect, Matrix.ScaleToFit.FILL);
float scale = Math.max((float) viewHeight / mPreviewSize.getHeight(),(float) viewWidth / mPreviewSize.getWidth());
matrix.postScale(scale, scale, centerX, centerY);
matrix.postRotate(90 * (rotation - 2), centerX, centerY);
}else if (Surface.ROTATION_0 == rotation) {
bufferRect.offset(centerX - bufferRect.centerX(), centerY - bufferRect.centerY());
matrix.setRectToRect(viewRect, bufferRect, Matrix.ScaleToFit.FILL);
float scale=Math.max((float) viewWidth / mPreviewSize.getWidth(), (float) viewHeight / mPreviewSize.getHeight());
matrix.postScale(scale, scale, centerX, centerY);
matrix.postRotate(0, centerX, centerY);
}else if(Surface.ROTATION_180== rotation){
bufferRect.offset(centerX - bufferRect.centerX(), centerY - bufferRect.centerY());
matrix.setRectToRect(viewRect, bufferRect, Matrix.ScaleToFit.FILL);
float scale=Math.max((float) viewWidth / mPreviewSize.getWidth(), (float) viewHeight / mPreviewSize.getHeight());
matrix.postScale(scale, scale, centerX, centerY);
matrix.postRotate(0, centerX, centerY);
}
try {
mTextureView.setTransform(matrix);
}catch (Exception ex){ex.printStackTrace();}finally {
bufferRect=null;
viewRect=null;
matrix=null;
}
}
Finally to Start Preview
private void startPreview() {
if (null == mCameraDevice || !mTextureView.isAvailable() || null == mPreviewSize) {
return;
}
try {
closePreviewSession();
SurfaceTexture texture = mTextureView.getSurfaceTexture();
assert texture != null;
texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
//texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
mPreviewBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
Surface previewSurface = new Surface(texture);
mPreviewBuilder.addTarget(previewSurface);
mCameraDevice.createCaptureSession(Collections.singletonList(previewSurface),
new CameraCaptureSession.StateCallback() {
#Override
public void onConfigured(#NonNull CameraCaptureSession session) {
mPreviewSession = session;
updatePreview();
}
#Override
public void onConfigureFailed(#NonNull CameraCaptureSession session) {
Toast.makeText(getActivityContext, "Failed", Toast.LENGTH_SHORT).show();
}
}, mBackgroundHandler);
//previewSurface=null;
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
I have created custom camera (Compulsory in horizontal mode) to capture image.
But I'm getting problem in some devices like motorola g3 and g4, it works perfectly on samsung device.
Can some help to fix this.
I resolved above problem by creating custom activity file without xml,
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Requires RelativeLayout.
mLayout = new RelativeLayout(this);
setContentView(mLayout);
}
#Override
protected void onResume() {
super.onResume();
//Outter layout
// Set the second argument by your choice.
// Usually, 0 for back-facing camera, 1 for front-facing camera.
// If the OS is pre-gingerbreak, this does not have any effect.
mPreview = new CameraPreview(this, 0, CameraPreview.LayoutMode.FitToParent);
RelativeLayout.LayoutParams previewLayoutParams = new RelativeLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
mLayout.addView(mPreview, 0, previewLayoutParams);
//Capture button
captureButton = new Button(this);
RelativeLayout.LayoutParams bParams = new RelativeLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
bParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM, RelativeLayout.TRUE);
bParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT, RelativeLayout.TRUE);
captureButton.setText("Capture");
mLayout.addView(captureButton, bParams);
captureButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mPreview.takePhoto();
}
});
}
Now create CameraPreview class
public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
private static boolean DEBUGGING = true;
private static final String LOG_TAG = "CameraPreviewSample";
private static final String CAMERA_PARAM_ORIENTATION = "orientation";
private static final String CAMERA_PARAM_LANDSCAPE = "landscape";
private static final String CAMERA_PARAM_PORTRAIT = "portrait";
protected Activity mActivity;
private SurfaceHolder mHolder;
protected Camera mCamera;
protected List<Camera.Size> mPreviewSizeList;
protected List<Camera.Size> mPictureSizeList;
protected Camera.Size mPreviewSize;
protected Camera.Size mPictureSize;
private int mSurfaceChangedCallDepth = 0;
private int mCameraId;
private LayoutMode mLayoutMode;
private int mCenterPosX = -1;
private int mCenterPosY;
private CameraApp cameraApp;
private ImageCallback imageCallback;
private List<Camera.Size> mSupportedPreviewSizes;
PreviewReadyCallback mPreviewReadyCallback = null;
public static enum LayoutMode {
FitToParent, // Scale to the size that no side is larger than the parent
NoBlank // Scale to the size that no side is smaller than the parent
};
public interface PreviewReadyCallback {
public void onPreviewReady();
}
/**
* State flag: true when surface's layout size is set and surfaceChanged()
* process has not been completed.
*/
protected boolean mSurfaceConfiguring = false;
public CameraPreview(Activity activity, int cameraId, LayoutMode mode) {
super(activity); // Always necessary
mActivity = activity;
mLayoutMode = mode;
mHolder = getHolder();
mHolder.addCallback(this);
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
if (Camera.getNumberOfCameras() > cameraId) {
mCameraId = cameraId;
} else {
mCameraId = 0;
}
} else {
mCameraId = 0;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
mCamera = Camera.open(mCameraId);
} else {
mCamera = Camera.open();
}
mSupportedPreviewSizes = mCamera.getParameters().getSupportedPreviewSizes();
Camera.Parameters cameraParams = mCamera.getParameters();
mPreviewSizeList = cameraParams.getSupportedPreviewSizes();
mPictureSizeList = cameraParams.getSupportedPictureSizes();
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
try {
mCamera.setPreviewDisplay(mHolder);
} catch (IOException e) {
mCamera.release();
mCamera = null;
}
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
mSurfaceChangedCallDepth++;
doSurfaceChanged(width, height);
mSurfaceChangedCallDepth--;
}
private void doSurfaceChanged(int width, int height) {
mCamera.stopPreview();
Camera.Parameters cameraParams = mCamera.getParameters();
boolean portrait = isPortrait();
// The code in this if-statement is prevented from executed again when surfaceChanged is
// called again due to the change of the layout size in this if-statement.
if (!mSurfaceConfiguring) {
Camera.Size previewSize = determinePreviewSize(portrait, width, height);
Camera.Size pictureSize = determinePictureSize(previewSize);
if (DEBUGGING) { Log.v(LOG_TAG, "Desired Preview Size - w: " + width + ", h: " + height); }
mPreviewSize = previewSize;
mPictureSize = pictureSize;
mSurfaceConfiguring = adjustSurfaceLayoutSize(previewSize, portrait, width, height);
// Continue executing this method if this method is called recursively.
// Recursive call of surfaceChanged is very special case, which is a path from
// the catch clause at the end of this method.
// The later part of this method should be executed as well in the recursive
// invocation of this method, because the layout change made in this recursive
// call will not trigger another invocation of this method.
if (mSurfaceConfiguring && (mSurfaceChangedCallDepth <= 1)) {
return;
}
}
configureCameraParameters(cameraParams, portrait);
mSurfaceConfiguring = false;
try {
mCamera.startPreview();
} catch (Exception e) {
Log.w(LOG_TAG, "Failed to start preview: " + e.getMessage());
// Remove failed size
mPreviewSizeList.remove(mPreviewSize);
mPreviewSize = null;
// Reconfigure
if (mPreviewSizeList.size() > 0) { // prevent infinite loop
surfaceChanged(null, 0, width, height);
} else {
Toast.makeText(mActivity, "Can't start preview", Toast.LENGTH_LONG).show();
Log.w(LOG_TAG, "Gave up starting preview");
}
}
if (null != mPreviewReadyCallback) {
mPreviewReadyCallback.onPreviewReady();
}
}
/**
* #param portrait
* #param reqWidth must be the value of the parameter passed in surfaceChanged
* #param reqHeight must be the value of the parameter passed in surfaceChanged
* #return Camera.Size object that is an element of the list returned from Camera.Parameters.getSupportedPreviewSizes.
*/
protected Camera.Size determinePreviewSize(boolean portrait, int reqWidth, int reqHeight) {
// Meaning of width and height is switched for preview when portrait,
// while it is the same as user's view for surface and metrics.
// That is, width must always be larger than height for setPreviewSize.
int reqPreviewWidth; // requested width in terms of camera hardware
int reqPreviewHeight; // requested height in terms of camera hardware
if (portrait) {
reqPreviewWidth = reqHeight;
reqPreviewHeight = reqWidth;
} else {
reqPreviewWidth = reqWidth;
reqPreviewHeight = reqHeight;
}
if (DEBUGGING) {
Log.v(LOG_TAG, "Listing all supported preview sizes");
for (Camera.Size size : mPreviewSizeList) {
Log.v(LOG_TAG, " w: " + size.width + ", h: " + size.height);
}
Log.v(LOG_TAG, "Listing all supported picture sizes");
for (Camera.Size size : mPictureSizeList) {
Log.v(LOG_TAG, " w: " + size.width + ", h: " + size.height);
}
}
// Adjust surface size with the closest aspect-ratio
float reqRatio = ((float) reqPreviewWidth) / reqPreviewHeight;
float curRatio, deltaRatio;
float deltaRatioMin = Float.MAX_VALUE;
Camera.Size retSize = null;
for (Camera.Size size : mPreviewSizeList) {
curRatio = ((float) size.width) / size.height;
deltaRatio = Math.abs(reqRatio - curRatio);
if (deltaRatio < deltaRatioMin) {
deltaRatioMin = deltaRatio;
retSize = size;
}
}
return retSize;
}
protected Camera.Size determinePictureSize(Camera.Size previewSize) {
Camera.Size retSize = null;
for (Camera.Size size : mPictureSizeList) {
if (size.equals(previewSize)) {
return size;
}
}
if (DEBUGGING) { Log.v(LOG_TAG, "Same picture size not found."); }
// if the preview size is not supported as a picture size
float reqRatio = ((float) previewSize.width) / previewSize.height;
float curRatio, deltaRatio;
float deltaRatioMin = Float.MAX_VALUE;
for (Camera.Size size : mPictureSizeList) {
curRatio = ((float) size.width) / size.height;
deltaRatio = Math.abs(reqRatio - curRatio);
if (deltaRatio < deltaRatioMin) {
deltaRatioMin = deltaRatio;
retSize = size;
}
}
return retSize;
}
protected boolean adjustSurfaceLayoutSize(Camera.Size previewSize, boolean portrait,
int availableWidth, int availableHeight) {
float tmpLayoutHeight, tmpLayoutWidth;
if (portrait) {
tmpLayoutHeight = previewSize.width;
tmpLayoutWidth = previewSize.height;
} else {
tmpLayoutHeight = previewSize.height;
tmpLayoutWidth = previewSize.width;
}
float factH, factW, fact;
factH = availableHeight / tmpLayoutHeight;
factW = availableWidth / tmpLayoutWidth;
if (mLayoutMode == LayoutMode.FitToParent) {
// Select smaller factor, because the surface cannot be set to the size larger than display metrics.
if (factH < factW) {
fact = factH;
} else {
fact = factW;
}
} else {
if (factH < factW) {
fact = factW;
} else {
fact = factH;
}
}
RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams)this.getLayoutParams();
int layoutHeight = (int) (tmpLayoutHeight * fact);
int layoutWidth = (int) (tmpLayoutWidth * fact);
if (DEBUGGING) {
Log.v(LOG_TAG, "Preview Layout Size - w: " + layoutWidth + ", h: " + layoutHeight);
Log.v(LOG_TAG, "Scale factor: " + fact);
}
boolean layoutChanged;
if ((layoutWidth != this.getWidth()) || (layoutHeight != this.getHeight())) {
layoutParams.height = layoutHeight;
layoutParams.width = layoutWidth;
if (mCenterPosX >= 0) {
layoutParams.topMargin = mCenterPosY - (layoutHeight / 2);
layoutParams.leftMargin = mCenterPosX - (layoutWidth / 2);
}
this.setLayoutParams(layoutParams); // this will trigger another surfaceChanged invocation.
layoutChanged = true;
} else {
layoutChanged = false;
}
return layoutChanged;
}
/**
* #param x X coordinate of center position on the screen. Set to negative value to unset.
* #param y Y coordinate of center position on the screen.
*/
public void setCenterPosition(int x, int y) {
mCenterPosX = x;
mCenterPosY = y;
}
protected void configureCameraParameters(Camera.Parameters cameraParams, boolean portrait) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.FROYO) { // for 2.1 and before
if (portrait) {
//cameraParams.set(CAMERA_PARAM_ORIENTATION, CAMERA_PARAM_PORTRAIT);
cameraParams.set(CAMERA_PARAM_ORIENTATION, CAMERA_PARAM_LANDSCAPE);
} else {
cameraParams.set(CAMERA_PARAM_ORIENTATION, CAMERA_PARAM_LANDSCAPE);
}
} else { // for 2.2 and later
int angle;
Display display = mActivity.getWindowManager().getDefaultDisplay();
switch (display.getRotation()) {
case Surface.ROTATION_0: // This is display orientation
angle = 90; // This is camera orientation
break;
case Surface.ROTATION_90:
angle = 0;
break;
case Surface.ROTATION_180:
angle = 270;
break;
case Surface.ROTATION_270:
angle = 180;
break;
default:
angle = 90;
break;
}
Log.v(LOG_TAG, "angle: " + angle);
mCamera.setDisplayOrientation(angle);
}
cameraParams.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
cameraParams.setPictureSize(mPictureSize.width, mPictureSize.height);
if (DEBUGGING) {
Log.v(LOG_TAG, "Preview Actual Size - w: " + mPreviewSize.width + ", h: " + mPreviewSize.height);
Log.v(LOG_TAG, "Picture Actual Size - w: " + mPictureSize.width + ", h: " + mPictureSize.height);
}
if (cameraParams.getSupportedFocusModes().contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO)) {
cameraParams.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);
}
mCamera.setParameters(cameraParams);
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
stop();
}
public void stop() {
if (null == mCamera) {
return;
}
mCamera.stopPreview();
mCamera.release();
mCamera = null;
}
public boolean isPortrait() {
return (mActivity.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT);
}
public void setOneShotPreviewCallback(PreviewCallback callback) {
if (null == mCamera) {
return;
}
mCamera.setOneShotPreviewCallback(callback);
}
public void setPreviewCallback(PreviewCallback callback) {
if (null == mCamera) {
return;
}
mCamera.setPreviewCallback(callback);
}
public Camera.Size getPreviewSize() {
return mPreviewSize;
}
public void setOnPreviewReady(PreviewReadyCallback cb) {
mPreviewReadyCallback = cb;
}
public void takePhoto(){
if(mCamera != null){
mCamera.takePicture(null, null, new Camera.PictureCallback() {
#Override
public void onPictureTaken(byte[] bytes, Camera camera) {
if(bytes.length > 0){
Log.d("CameraPreview","Yes!!! We got an Image");
}
Bitmap capturedBitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
//capturedBitmap = Bitmap.createScaledBitmap(capturedBitmap,1196,527,true);
imageCallback = (ImageCallback) mActivity;
imageCallback.resultImage(capturedBitmap);
}
});
}
}
Thats it !!, you are almost done
Just one more thing that you need to create, is interface for getting result in your main activity.
Pls. feel free to ask your query.
Use property android:adjustViewBounds="true"
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:adjustViewBounds="true"
android:visibility="gone" />
I want to show a video preview in my galaxy tab which has surface view of 1200X800 when I try to set the camera parameters with that value, it just does not take, if it does not take, it shows me squeezed videos. After finding the issue, I found that, it takes the resolution of 1024X768, I am not sure why its not taking it.
Code for Camera
class CameraView extends SurfaceView implements SurfaceHolder.Callback, PreviewCallback {
private SurfaceHolder mHolder;
private Camera mCamera;
private Camera.Size previewSize;
private List<Camera.Size> mSupportedPreviewSizes;
public CameraView(Context context, Camera camera) {
super(context);
Log.w("camera", "camera view");
mCamera = camera;
mSupportedPreviewSizes = mCamera.getParameters(). getSupportedPreviewSizes();
mHolder = getHolder();
mHolder.addCallback(CameraView.this);
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
mCamera.setPreviewCallback(CameraView.this);
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
try {
stopPreview();
mCamera.setPreviewDisplay(holder);
} catch (IOException exception) {
mCamera.release();
mCamera = null;
}
}
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
stopPreview();
Camera.Parameters camParams = mCamera.getParameters();
List<Camera.Size> sizes = camParams.getSupportedPictureSizes();
camParams.setPictureSize(sizes.get(0).width, sizes.get(0).height);
List<Camera.Size> size = camParams.getSupportedPreviewSizes();
// Sort the list in ascending order
Collections.sort(size, new Comparator<Camera.Size>() {
public int compare(final Camera.Size a, final Camera.Size b) {
return a.width * a.height - b.width * b.height;
}
});
// Pick the first preview size that is equal or bigger, or pick the last (biggest) option if we cannot
// reach the initial settings of imageWidth/imageHeight.
for (int i = 0; i < size.size(); i++) {
if ((size.get(i).width >= imageWidth && sizes.get(i).height >= imageHeight) || i == size.size() - 1) {
imageWidth = size.get(i).width;
imageHeight = size.get(i).height;
Log.v(LOG_TAG, "Changed to supported resolution: " + imageWidth + "x" + imageHeight);
break;
}
}
camParams.setPreviewSize(imageWidth, imageHeight);
Log.v(LOG_TAG, "Setting imageWidth: " + imageWidth + " imageHeight: " + imageHeight + " frameRate: " + frameRate);
camParams.setPreviewFrameRate(frameRate);
Log.v(LOG_TAG, "Preview Framerate: " + camParams.getPreviewFrameRate());
mCamera.setParameters(camParams);
// Set the holder (which might have changed) again
try {
mCamera.setPreviewDisplay(holder);
mCamera.setPreviewCallback(CameraView.this);
startPreview();
} catch (Exception e) {
Log.e(LOG_TAG, "Could not set preview display in surfaceChanged");
}
}
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;
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int width = resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec);
final int height = resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec);
Log.v(LOG_TAG, width + " and " + height);
setMeasuredDimension(width, height);
if (previewSize != null) {
previewSize =
getOptimalPreviewSize(mSupportedPreviewSizes, width, height);
}
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
try {
mHolder.addCallback(null);
mCamera.setPreviewCallback(null);
} catch (RuntimeException e) {
// The camera has probably just been released, ignore.
}
}
public void startPreview() {
if (!isPreviewOn && mCamera != null) {
isPreviewOn = true;
mCamera.startPreview();
}
}
public void stopPreview() {
if (isPreviewOn && mCamera != null) {
isPreviewOn = false;
mCamera.stopPreview();
}
}
#Override
public void onPreviewFrame(byte[] data, Camera camera) {
if (audioRecord == null || audioRecord.getRecordingState() != AudioRecord.RECORDSTATE_RECORDING) {
startTime = System.currentTimeMillis();
return;
}
if (RECORD_LENGTH > 0) {
int i = imagesIndex++ % images.length;
yuvImage = images[i];
timestamps[i] = 1000 * (System.currentTimeMillis() - startTime);
}
/* get video data */
if (yuvImage != null && recording) {
((ByteBuffer)yuvImage.image[0].position(0)).put(data);
if (RECORD_LENGTH <= 0) try {
Log.v(LOG_TAG, "Writing Frame");
long t = 1000 * (System.currentTimeMillis() - startTime);
if (t > recorder.getTimestamp()) {
recorder.setTimestamp(t);
}
recorder.record(yuvImage);
} catch (FFmpegFrameRecorder.Exception e) {
Log.v(LOG_TAG, e.getMessage());
e.printStackTrace();
}
}
}
}
I also tried with getOptimalPreviewSize however, id did not work either.
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 7 years ago.
Improve this question
Please tell me how to create a square size camera in my android application as in instagram? I was looking for a solution for a very long time, but I couldn't find it.
You can not create an square size camera its totally depends upon the device.
Basically every device have their own camera sizes you can get the
list of all the device's size and then set your prefer one...!
Here is a camera preview class which auto set the good size to the camera view,
import java.io.IOException;
import java.util.List;
import android.content.res.Configuration;
import android.hardware.Camera;
import android.hardware.Camera.PreviewCallback;
import android.os.Build;
import android.util.Log;
import android.view.Display;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.widget.RelativeLayout;
import android.widget.Toast;
/**
* This class assumes the parent layout is RelativeLayout.LayoutParams.
*/
#SuppressWarnings("deprecation")
public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
private static boolean DEBUGGING = true;
private static final String LOG_TAG = "CameraPreviewSample";
private static final String CAMERA_PARAM_ORIENTATION = "orientation";
private static final String CAMERA_PARAM_LANDSCAPE = "landscape";
private static final String CAMERA_PARAM_PORTRAIT = "portrait";
protected CameraActivity mActivity;
private SurfaceHolder mHolder;
protected Camera mCamera;
protected List<Camera.Size> mPreviewSizeList;
protected List<Camera.Size> mPictureSizeList;
protected Camera.Size mPreviewSize;
protected Camera.Size mPictureSize;
private int mSurfaceChangedCallDepth = 0;
private int mCameraId;
private LayoutMode mLayoutMode;
private int mCenterPosX = -1;
private int mCenterPosY;
public static boolean isSafeForTakeImage = false;
PreviewReadyCallback mPreviewReadyCallback = null;
public static enum LayoutMode
{
FitToParent, // Scale to the size that no side is larger than the parent
NoBlank // Scale to the size that no side is smaller than the parent
};
public interface PreviewReadyCallback {
public void onPreviewReady();
}
/**
* State flag: true when surface's layout size is set and surfaceChanged()
* process has not been completed.
*/
protected boolean mSurfaceConfiguring = false;
public CameraPreview(CameraActivity activity, int cameraId, LayoutMode mode) {
super(activity); // Always necessary
mActivity = activity;
mLayoutMode = mode;
mHolder = getHolder();
mHolder.addCallback(this);
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
if (Camera.getNumberOfCameras() > cameraId) {
mCameraId = cameraId;
} else {
mCameraId = 0;
}
} else {
mCameraId = 0;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
mCamera = Camera.open(mCameraId);
} else {
mCamera = Camera.open();
}
Camera.Parameters cameraParams = mCamera.getParameters();
mPreviewSizeList = cameraParams.getSupportedPreviewSizes();
mPictureSizeList = cameraParams.getSupportedPictureSizes();
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
try
{
isSafeForTakeImage = true;
mCamera.setPreviewDisplay(mHolder);
} catch (IOException e) {
mCamera.release();
mCamera = null;
}
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
mSurfaceChangedCallDepth++;
doSurfaceChanged(width, height);
mSurfaceChangedCallDepth--;
}
private void doSurfaceChanged(int width, int height)
{
isSafeForTakeImage = false;
mCamera.stopPreview();
Camera.Parameters cameraParams = mCamera.getParameters();
// cameraParams.setFocusMode(Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
if(cameraParams.getSupportedFocusModes() != null
&& cameraParams.getSupportedFocusModes().contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE))
{
cameraParams.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
}
cameraParams.setRotation(90);
boolean portrait = isPortrait();
// The code in this if-statement is prevented from executed again when surfaceChanged is
// called again due to the change of the layout size in this if-statement.
if (!mSurfaceConfiguring) {
Camera.Size previewSize = determinePreviewSize(portrait, width, height);
Camera.Size pictureSize = determinePictureSize(previewSize);
if (DEBUGGING) { Log.v(LOG_TAG, "Desired Preview Size - w: " + width + ", h: " + height); }
mPreviewSize = previewSize;
mPictureSize = pictureSize;
// mSurfaceConfiguring = adjustSurfaceLayoutSize(previewSize, portrait, width, height);
// Continue executing this method if this method is called recursively.
// Recursive call of surfaceChanged is very special case, which is a path from
// the catch clause at the end of this method.
// The later part of this method should be executed as well in the recursive
// invocation of this method, because the layout change made in this recursive
// call will not trigger another invocation of this method.
if (mSurfaceConfiguring && (mSurfaceChangedCallDepth <= 1)) {
return;
}
}
configureCameraParameters(cameraParams, portrait);
mSurfaceConfiguring = false;
try {
mCamera.startPreview();
isSafeForTakeImage = true;
} catch (Exception e) {
Log.w(LOG_TAG, "Failed to start preview: " + e.getMessage());
// Remove failed size
mPreviewSizeList.remove(mPreviewSize);
mPreviewSize = null;
// Reconfigure
if (mPreviewSizeList.size() > 0) { // prevent infinite loop
surfaceChanged(null, 0, width, height);
} else {
Toast.makeText(mActivity, "Can't start preview", Toast.LENGTH_LONG).show();
Log.w(LOG_TAG, "Gave up starting preview");
}
}
if (null != mPreviewReadyCallback) {
mPreviewReadyCallback.onPreviewReady();
}
//TODO
mActivity.setMyCamera(mCamera);
}
/**
* #param cameraParams
* #param portrait
* #param reqWidth must be the value of the parameter passed in surfaceChanged
* #param reqHeight must be the value of the parameter passed in surfaceChanged
* #return Camera.Size object that is an element of the list returned from Camera.Parameters.getSupportedPreviewSizes.
*/
protected Camera.Size determinePreviewSize(boolean portrait, int reqWidth, int reqHeight) {
// Meaning of width and height is switched for preview when portrait,
// while it is the same as user's view for surface and metrics.
// That is, width must always be larger than height for setPreviewSize.
int reqPreviewWidth; // requested width in terms of camera hardware
int reqPreviewHeight; // requested height in terms of camera hardware
if (portrait) {
reqPreviewWidth = reqHeight;
reqPreviewHeight = reqWidth;
} else {
reqPreviewWidth = reqWidth;
reqPreviewHeight = reqHeight;
}
if (DEBUGGING) {
Log.v(LOG_TAG, "Listing all supported preview sizes");
for (Camera.Size size : mPreviewSizeList) {
Log.v(LOG_TAG, " w: " + size.width + ", h: " + size.height);
}
Log.v(LOG_TAG, "Listing all supported picture sizes");
for (Camera.Size size : mPictureSizeList) {
Log.v(LOG_TAG, " w: " + size.width + ", h: " + size.height);
}
}
// Adjust surface size with the closest aspect-ratio
float reqRatio = ((float) reqPreviewWidth) / reqPreviewHeight;
float curRatio, deltaRatio;
float deltaRatioMin = Float.MAX_VALUE;
Camera.Size retSize = null;
for (Camera.Size size : mPreviewSizeList) {
curRatio = ((float) size.width) / size.height;
deltaRatio = Math.abs(reqRatio - curRatio);
if (deltaRatio < deltaRatioMin) {
deltaRatioMin = deltaRatio;
retSize = size;
}
}
return retSize;
}
protected Camera.Size determinePictureSize(Camera.Size previewSize) {
Camera.Size retSize = null;
for (Camera.Size size : mPictureSizeList) {
if (size.equals(previewSize)) {
return size;
}
}
if (DEBUGGING) { Log.v(LOG_TAG, "Same picture size not found."); }
// if the preview size is not supported as a picture size
float reqRatio = ((float) previewSize.width) / previewSize.height;
float curRatio, deltaRatio;
float deltaRatioMin = Float.MAX_VALUE;
for (Camera.Size size : mPictureSizeList) {
curRatio = ((float) size.width) / size.height;
deltaRatio = Math.abs(reqRatio - curRatio);
if (deltaRatio < deltaRatioMin) {
deltaRatioMin = deltaRatio;
retSize = size;
}
}
return retSize;
}
protected boolean adjustSurfaceLayoutSize(Camera.Size previewSize, boolean portrait,
int availableWidth, int availableHeight) {
float tmpLayoutHeight, tmpLayoutWidth;
if (portrait) {
tmpLayoutHeight = previewSize.width;
tmpLayoutWidth = previewSize.height;
} else {
tmpLayoutHeight = previewSize.height;
tmpLayoutWidth = previewSize.width;
}
float factH, factW, fact;
factH = availableHeight / tmpLayoutHeight;
factW = availableWidth / tmpLayoutWidth;
if (mLayoutMode == LayoutMode.FitToParent) {
// Select smaller factor, because the surface cannot be set to the size larger than display metrics.
if (factH < factW) {
fact = factH;
} else {
fact = factW;
}
} else {
if (factH < factW) {
fact = factW;
} else {
fact = factH;
}
}
RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams)this.getLayoutParams();
int layoutHeight = (int) (tmpLayoutHeight * fact);
int layoutWidth = (int) (tmpLayoutWidth * fact);
if (DEBUGGING) {
Log.v(LOG_TAG, "Preview Layout Size - w: " + layoutWidth + ", h: " + layoutHeight);
Log.v(LOG_TAG, "Scale factor: " + fact);
}
boolean layoutChanged;
if ((layoutWidth != this.getWidth()) || (layoutHeight != this.getHeight())) {
layoutParams.height = layoutHeight;
layoutParams.width = layoutWidth;
if (mCenterPosX >= 0) {
layoutParams.topMargin = mCenterPosY - (layoutHeight / 2);
layoutParams.leftMargin = mCenterPosX - (layoutWidth / 2);
}
this.setLayoutParams(layoutParams); // this will trigger another surfaceChanged invocation.
layoutChanged = true;
} else {
layoutChanged = false;
}
return layoutChanged;
}
/**
* #param x X coordinate of center position on the screen. Set to negative value to unset.
* #param y Y coordinate of center position on the screen.
*/
public void setCenterPosition(int x, int y) {
mCenterPosX = x;
mCenterPosY = y;
}
protected void configureCameraParameters(Camera.Parameters cameraParams, boolean portrait) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.FROYO) { // for 2.1 and before
if (portrait)
{
cameraParams.set(CAMERA_PARAM_ORIENTATION, CAMERA_PARAM_PORTRAIT);
}
else
{
cameraParams.set(CAMERA_PARAM_ORIENTATION, CAMERA_PARAM_LANDSCAPE);
}
} else { // for 2.2 and later
int angle;
Display display = mActivity.getWindowManager().getDefaultDisplay();
switch (display.getRotation()) {
case Surface.ROTATION_0: // This is display orientation
angle = 90; // This is camera orientation
break;
case Surface.ROTATION_90:
angle = 0;
break;
case Surface.ROTATION_180:
angle = 270;
break;
case Surface.ROTATION_270:
angle = 180;
break;
default:
angle = 90;
break;
}
Log.v(LOG_TAG, "angle: " + angle);
mCamera.setDisplayOrientation(angle);
}
cameraParams.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
cameraParams.setPictureSize(mPictureSize.width, mPictureSize.height);
if (DEBUGGING) {
Log.v(LOG_TAG, "Preview Actual Size - w: " + mPreviewSize.width + ", h: " + mPreviewSize.height);
Log.v(LOG_TAG, "Picture Actual Size - w: " + mPictureSize.width + ", h: " + mPictureSize.height);
}
mCamera.setParameters(cameraParams);
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
stop();
isSafeForTakeImage = false;
}
public void stop() {
if (null == mCamera) {
return;
}
mCamera.stopPreview();
mCamera.release();
mCamera = null;
}
public boolean isPortrait() {
return (mActivity.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT);
}
public void setOneShotPreviewCallback(PreviewCallback callback) {
if (null == mCamera) {
return;
}
mCamera.setOneShotPreviewCallback(callback);
}
public void setPreviewCallback(PreviewCallback callback) {
if (null == mCamera) {
return;
}
mCamera.setPreviewCallback(callback);
}
public Camera.Size getPreviewSize() {
return mPreviewSize;
}
public void setOnPreviewReady(PreviewReadyCallback cb) {
mPreviewReadyCallback = cb;
}
}
You need the below permissions in your AndroidManifest.xml
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature
android:name="android.hardware.camera"
android:required="false" />
<uses-feature
android:name="android.hardware.camera.autofocus"
android:required="false" />
The above class assume, you are using relative layout,
In your activity put this code in your onResume() method,
surfaceview = new CameraPreview(this, 0, CameraPreview.LayoutMode.FitToParent);
LayoutParams previewLayoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.FILL_PARENT);
previewLayoutParams.addRule(RelativeLayout.CENTER_HORIZONTAL);
relativeLayout.addView(surfaceview, 0, previewLayoutParams);
Add below lines in your onPause() method
try{
relativeLayout.removeView(surfaceview);
}catch(Exception exception){
}
I want to get a full screen portrait camera preview. Using Google's sample, the preview is in landscape format across the portrait screen. When I set to the preview to 480 x 864, the reverse of working landscape dimensions, I get a setParameters failed error.
I have read all the StackOverflow I can find, but I can't figure why dimensions supported in landscape fail in portrait. Nor can I find a way to force full screen portrait preview. The Camera app on the phone provides full screen portrait preview so I know it is not a hardware limitation.
I'm testing with a Droid 3 running Android 2.3.3
I would appreciate any suggestions.
public class SimplePreview extends ViewGroup implements SurfaceHolder.Callback {
private final String TAG = "Preview";
SurfaceView mSurfaceView;
SurfaceHolder mHolder;
Size mPreviewSize;
List<Size> mSupportedPreviewSizes;
Camera mCamera;
int cameraId;
Activity activity;
public SimplePreview(Activity context, int defaultCamera) {
super(context);
activity = context;
cameraId = defaultCamera;
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();
}
}
public void switchCamera(Camera camera) {
setCamera(camera);
try {
camera.setPreviewDisplay(mHolder);
} catch (IOException exception) {
Log.e(TAG, "IOException caused by setPreviewDisplay()", exception);
}
Camera.Parameters parameters = camera.getParameters();
parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
requestLayout();
camera.setParameters(parameters);
}
#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);
Log.i(TAG, "setting View measured dimensions to width: " + width
+ " height: " + height);
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 surfaceDestroyed(SurfaceHolder holder) {
// Surface will be destroyed when we return, so stop the preview.
if (mCamera != null) {
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) {
Log.v(TAG, " width: " + size.width + " height: " + size.height);
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.i(TAG, "optimal preview width: " + optimalSize.width + " height: "
+ optimalSize.height);
return optimalSize;
}
public void surfaceCreated(SurfaceHolder holder) {
// The Surface has been created, acquire the camera and tell it where
// to draw.
try {
if (mCamera != null) {
final int width = getWidth();
final int height = getHeight();
Log.i(TAG, "view width: " + width + " height: " + height);
if (height > width) {
Log.i(TAG, "in portrait mode so rotate camera preview");
// THis line fixed the camera display orientation. seems to
// have to be called before setPreviewDisplay()
mCamera.setDisplayOrientation(90);
}
mCamera.setPreviewDisplay(holder);
}
} catch (IOException exception) {
Log.e(TAG, "IOException caused by setPreviewDisplay()", exception);
}
}
/**
* orientation and rotation work done here. Now that the size is known, set
* up the camera parameters and begin the preview.
*/
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
if (mCamera != null) {
mCamera.stopPreview();
Camera.Parameters parameters = mCamera.getParameters();
// mCamera.setDisplayOrientation(90);
final int width = getWidth();
final int height = getHeight();
Log.i(TAG, "view width: " + width + " height: " + height);
if (height > width) {
Log.i(TAG, "portrait: setting preview width: " + 480
+ " height: " + 864);
parameters.setPreviewSize(480, 864);
// had no effect
parameters.set("orientation", "portrait");
} else {
Log.i(TAG, "landscape: setting preview width: "
+ mPreviewSize.width + " height: "
+ mPreviewSize.height);
parameters.setPreviewSize(mPreviewSize.width,
mPreviewSize.height);
}
requestLayout();
// *** following line throws setParameters failed error ***
mCamera.setParameters(parameters);
mCamera.startPreview();
}
}
}
And the call from the Activity's onCreate():
preview = new SimplePreview(this, defaultCameraId);
setContentView(preview);
try this code:
package com.example.dragme;
import java.io.IOException;
import java.util.List;
import android.app.Activity;
import android.content.Context;
import android.content.res.Configuration;
import android.hardware.Camera;
import android.os.Build;
import android.util.Log;
import android.view.Display;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
private SurfaceHolder mHolder;
public Camera mCamera;
private static boolean DEBUGGING = true;
private static final String LOG_TAG = "CameraPreviewSample";
private static final String CAMERA_PARAM_ORIENTATION = "orientation";
private static final String CAMERA_PARAM_LANDSCAPE = "landscape";
private static final String CAMERA_PARAM_PORTRAIT = "portrait";
protected Activity mActivity;
protected List<Camera.Size> mPreviewSizeList;
protected List<Camera.Size> mPictureSizeList;
protected Camera.Size mPreviewSize;
protected Camera.Size mPictureSize;
public CameraPreview(Context context, Camera camera) {
super(context);
mActivity=(Activity)context;
mCamera = camera;
// 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) {
// The Surface has been created, now tell the camera where to draw the preview.
try {
mCamera.setPreviewDisplay(holder);
mCamera.startPreview();
} catch (IOException e) {
Log.d("CameraView", "Error setting camera preview: " + e.getMessage());
}
}
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) {
// 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 cameraParams = mCamera.getParameters();
boolean portrait = isPortrait();
configureCameraParameters(cameraParams, portrait);
mCamera.setPreviewDisplay(mHolder);
mCamera.startPreview();
} catch (Exception e){
Log.d("CameraView", "Error starting camera preview: " + e.getMessage());
}
}
public void onPause() {
if (null == mCamera) {
return;
}
mCamera.stopPreview();
mCamera.release();
mCamera = null;
}
protected void configureCameraParameters(Camera.Parameters cameraParams, boolean portrait) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.FROYO) { // for 2.1 and before
if (portrait) {
cameraParams.set(CAMERA_PARAM_ORIENTATION, CAMERA_PARAM_PORTRAIT);
} else {
cameraParams.set(CAMERA_PARAM_ORIENTATION, CAMERA_PARAM_LANDSCAPE);
}
} else { // for 2.2 and later
int angle;
Display display = mActivity.getWindowManager().getDefaultDisplay();
switch (display.getRotation()) {
case Surface.ROTATION_0: // This is display orientation
angle = 90; // This is camera orientation
break;
case Surface.ROTATION_90:
angle = 0;
break;
case Surface.ROTATION_180:
angle = 270;
break;
case Surface.ROTATION_270:
angle = 180;
break;
default:
angle = 90;
break;
}
Log.v(LOG_TAG, "angle: " + angle);
mCamera.setDisplayOrientation(angle);
}
cameraParams.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
cameraParams.setPictureSize(mPictureSize.width, mPictureSize.height);
if (DEBUGGING) {
Log.v(LOG_TAG, "Preview Actual Size - w: " + mPreviewSize.width + ", h: " + mPreviewSize.height);
Log.v(LOG_TAG, "Picture Actual Size - w: " + mPictureSize.width + ", h: " + mPictureSize.height);
}
mCamera.setParameters(cameraParams);
}
public boolean isPortrait() {
return (mActivity.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT);
}
}