i am working on camera2 and have got a problem with rotation.
In some devices, when switching to front camera, preview is being rotated 180 degrees.
Was looking such a "setDefaultOrientation()" method for camera2 but couldn't find it.
Thanks
With a GLSurfaceView, you're presumably using a SurfaceTexture to map the camera preview stream into a GL texture, and then rendering it.
The image data in the texture is not automatically rotated to be 'upright' - for one, there's no way for the OS to know how you're going to render your texture.
However, the SurfaceTexture has a getTransformMatrix call, which when connected to the camera2 API, will give you the necessary transform from the camera's orientation to the device's native orientation (typically portrait for phones, landscape for tablets, but not always). You'll then have to add the transform from the native orientation to your app's orientation to this matrix, and then apply the combined matrix transform to adjust the preview image.
you can set the correct image rotation in CaptureRequest object while capturing an image in CameraCaptureSession.StateCallback using CameraCharacterisitics
CameraCharacterisitics cameraCharacteristics = cameraManager.getCameraCharacteristics(cameraId);
like this.
private CameraCaptureSession.StateCallback sessionStateCallback = new CameraCaptureSession.StateCallback() {
#Override
public void onConfigured(#NonNull CameraCaptureSession session) {
captureSession = session;
try {
CaptureRequest.Builder builder = session.getDevice().createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
builder.addTarget(imageReader.getSurface());
builder.set(CaptureRequest.JPEG_ORIENTATION, cameraCharacteristics.get(CameraCharacteristics.SENSOR_ORIENTATION));
session.capture(builder.build(), null, backgroundHandler);
} catch (CameraAccessException e) {
Log.d("tag", e.getMessage());
}
}
#Override
public void onConfigureFailed(#NonNull CameraCaptureSession session) {
}
};
you can try this code for open your camera
I hope this will help
public void startPreview() {
try {
Log.i(TAG, "starting preview: " + started);
// ....
Camera.CameraInfo camInfo = new Camera.CameraInfo();
Camera.getCameraInfo(cameraIndex, camInfo);
int cameraRotationOffset = camInfo.orientation;
// ...
Camera.Parameters parameters = camera.getParameters();
List<Camera.Size> previewSizes = parameters.getSupportedPreviewSizes();
Camera.Size previewSize = null;
float closestRatio = Float.MAX_VALUE;
int targetPreviewWidth = isLandscape() ? getWidth() : getHeight();
int targetPreviewHeight = isLandscape() ? getHeight() : getWidth();
float targetRatio = targetPreviewWidth / (float) targetPreviewHeight;
Log.v(TAG, "target size: " + targetPreviewWidth + " / " + targetPreviewHeight + " ratio:" + targetRatio);
for (Camera.Size candidateSize : previewSizes) {
float whRatio = candidateSize.width / (float) candidateSize.height;
if (previewSize == null || Math.abs(targetRatio - whRatio) < Math.abs(targetRatio - closestRatio)) {
closestRatio = whRatio;
previewSize = candidateSize;
}
}
int rotation = getWindowManager().getDefaultDisplay().getRotation();
int degrees = 0;
switch (rotation) {
case Surface.ROTATION_0:
degrees = 0;
break; // Natural orientation
case Surface.ROTATION_90:
degrees = 90;
break; // Landscape left
case Surface.ROTATION_180:
degrees = 180;
break;// Upside down
case Surface.ROTATION_270:
degrees = 270;
break;// Landscape right
}
int displayRotation;
if (isFrontFacingCam) {
displayRotation = (cameraRotationOffset + degrees) % 360;
displayRotation = (360 - displayRotation) % 360; // compensate
// the
// mirror
} else { // back-facing
displayRotation = (cameraRotationOffset - degrees + 360) % 360;
}
Log.v(TAG, "rotation cam / phone = displayRotation: " + cameraRotationOffset + " / " + degrees + " = "
+ displayRotation);
this.camera.setDisplayOrientation(displayRotation);
int rotate;
if (isFrontFacingCam) {
rotate = (360 + cameraRotationOffset + degrees) % 360;
} else {
rotate = (360 + cameraRotationOffset - degrees) % 360;
}
Log.v(TAG, "screenshot rotation: " + cameraRotationOffset + " / " + degrees + " = " + rotate);
Log.v(TAG, "preview size: " + previewSize.width + " / " + previewSize.height);
parameters.setPreviewSize(previewSize.width, previewSize.height);
parameters.setRotation(rotate);
camera.setParameters(parameters);
camera.setPreviewDisplay(mHolder);
camera.startPreview();
Log.d(TAG, "preview started");
started = true;
} catch (IOException e) {
Log.d(TAG, "Error setting camera preview: " + e.getMessage());
}
}
Related
I am capturing the image to transform it into a square of a certain size. When I capture an image using different phones, in some of them the image captured is the same as the image shown in the preview, but in some phones, the image captured is rotated.
Below is the code:
On button click:
bGetImage.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
final Bitmap[] bitmap = {null};
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
imageCapture.takePicture(getMainExecutor(),new ImageCapture.OnImageCapturedCallback() {
#Override
public void onCaptureSuccess(#NonNull ImageProxy image) {
super.onCaptureSuccess(image);
Log.d(TAG, "onCaptureSuccess: testview.innerRectangle " + testview.innerRectangle.width());
Log.d(TAG, "onCaptureSuccess: testview.innerRectangle " + testview.innerRectangle.height());
OutputTransform source = previewView.getOutputTransform();
// imageProxy is the output of the ImageCapture.
OutputTransform target = new ImageProxyTransformFactory().getOutputTransform(image);
//print size of target
Log.d(TAG, "onCaptureSuccess: target " + target.getMatrix().toString());
// Build the transform from ImageAnalysis to PreviewView
CoordinateTransform coordinateTransform = new CoordinateTransform(source, target);
// The variable box here is your bounding box in PreviewView
coordinateTransform.mapRect(testview.innerRectangle);
Log.d(TAG, "onCaptureSuccess: testview.innerRectangle " + testview.innerRectangle.width());
Log.d(TAG, "onCaptureSuccess: testview.innerRectangle " + testview.innerRectangle.height());
bitmap[0] = imageProxytoBitmap(image);
Log.d(TAG, "onCaptureSuccess: bitmap[0] " + bitmap[0].getWidth());
Log.d(TAG, "onCaptureSuccess: bitmap[0] " + bitmap[0].getHeight());
// bitmap[0] = Bitmap.createBitmap(bitmap[0],(int)testview.innerRectangle.left,(int)testview.innerRectangle.top,(int)testview.innerRectangle.width(),(int)testview.innerRectangle.height());
try {
Uri uri = getUri(bitmap[0], getContentResolver());
Log.d(TAG, "onCaptureSuccess: bitmap[0] " + bitmap[0].getWidth());
Log.d(TAG, "onCaptureSuccess: bitmap[0] " + bitmap[0].getHeight());
String path = uri.toString();
moveToScanActivity(path);
} catch (IOException e) {
Log.e(TAG, "onCaptureSuccess: ",e);
}
}
});
}
}
});
Camera Provider initialization:
cameraProviderFuture.addListener(() -> {
try {
ProcessCameraProvider cameraProvider = cameraProviderFuture.get();
CameraSelector selector = CameraSelector.DEFAULT_BACK_CAMERA;
cameraProvider.unbindAll();
// Camera camera = cameraProvider.bindToLifecycle(this, selector);
bindPreview(cameraProvider);
} catch (ExecutionException | InterruptedException e) {
// No errors need to be handled for this Future.
// This should never be reached.
}
}, ContextCompat.getMainExecutor(this));
Other functions I am using:
void bindPreview(#NonNull ProcessCameraProvider cameraProvider) {
Log.d(TAG, "bindPreview: width " + width);
Log.d(TAG, "bindPreview: height " + height);
Preview preview = new Preview.Builder()
.setTargetResolution(new Size(width, height))
.setTargetRotation(ROTATION_0)
.build();
previewView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
#Override
public void onLayoutChange(
View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
Size newViewFinderDimens = new Size(right - left, bottom - top);
updateTransform(0, MainActivity.bufferDimens, newViewFinderDimens);
}
});
imageCapture =
new ImageCapture.Builder()
.setTargetResolution(new Size(width, height))
.setTargetRotation(ROTATION_0)
.build();
CameraSelector cameraSelector = new CameraSelector.Builder()
.requireLensFacing(CameraSelector.LENS_FACING_BACK)
.build();
preview.setSurfaceProvider(previewView.getSurfaceProvider());
UseCaseGroup useCaseGroup = new UseCaseGroup.Builder()
.addUseCase(preview)
.addUseCase(imageCapture)
.build();
cameraProvider.bindToLifecycle((LifecycleOwner)this, cameraSelector, useCaseGroup);
previewHeight = previewView.getHeight();
previewWidth = previewView.getWidth();
}
private Uri getUri(Bitmap bitmap, ContentResolver contentResolver) throws FileNotFoundException {
File file = new File(getApplicationContext().getExternalMediaDirs()[0].getAbsolutePath(),"Mytittle_" + System.currentTimeMillis() + ".png");
bitmap.compress(Bitmap.CompressFormat.PNG, 100, new FileOutputStream(file));
Log.d(TAG, "getUri: path " + file.getAbsolutePath());
return Uri.parse(file.getAbsolutePath());
}
private Bitmap rotateImage(Bitmap img, int degree)
{
Matrix matrix = new Matrix();
matrix.postRotate(degree);
return Bitmap.createBitmap(img, 0, 0, img.getWidth(), img.getHeight(), matrix, false);
}
public void updateTransform(int rotation, Size newBufferDimens, Size newViewFinderDimens) {
if (rotation == viewFinderRotation &&
newBufferDimens == bufferDimens &&
newViewFinderDimens == viewFinderDimens) {
// Nothing has changed, no need to transform output again
//Log.d("AK", "nothing has changed, no need to transform output again");
return;
}
if (newBufferDimens.getWidth() == 0 || newBufferDimens.getHeight() == 0) {
// Invalid buffer dimens - wait for valid inputs before setting matrix
//Log.d("AK", "returning from here newbufferdimens");
return;
} else {
// Update internal field with new inputs
bufferDimens = newBufferDimens;
}
if (newViewFinderDimens.getWidth() == 0 || newViewFinderDimens.getHeight() == 0) {
// Invalid view finder dimens - wait for valid inputs before setting matrix
//Log.d("AK", "returning from here newViewfinder");
return;
} else {
// Update internal field with new inputs
viewFinderDimens = newViewFinderDimens;
}
Matrix matrix = new Matrix();
Log.d(TAG, "Applying output transformation.\n" +
"View finder size: " + viewFinderDimens + "\n" +
"Preview output size: " + bufferDimens + "\n" +
"View finder rotation: " + viewFinderRotation + "\n" +
"Preview output rotation: " + 0);
float centerX = previewView.getWidth() / 2f;
float centerY = previewView.getHeight() / 2f;
// Correct preview output to account for display rotation
float rotationDegrees;
switch (previewView.getDisplay().getRotation()) {
case Surface.ROTATION_0:
rotationDegrees = 0f;
break;
case Surface.ROTATION_90:
rotationDegrees = 90f;
break;
case Surface.ROTATION_180:
rotationDegrees = 180f;
break;
case Surface.ROTATION_270:
rotationDegrees = 270f;
break;
default:
return;
}
matrix.postRotate(-rotationDegrees, centerX, centerY);
float bufferRatio = bufferDimens.getHeight() / (float) bufferDimens.getWidth();
int scaledWidth;
int scaledHeight;
if (previewView.getWidth() > previewView.getHeight()) {
scaledHeight = viewFinderDimens.getWidth();
scaledWidth = round(viewFinderDimens.getWidth() * bufferRatio);
} else {
scaledHeight = viewFinderDimens.getHeight();
scaledWidth = round(viewFinderDimens.getHeight() * bufferRatio);
}
Canvas canvas = new Canvas();
float xScale = scaledWidth / (float) viewFinderDimens.getWidth();
float yScale = scaledHeight / (float) viewFinderDimens.getHeight();
//Log.d("AK", "scale x y" + xScale + " " + yScale);
matrix.preScale(xScale, yScale, centerX, centerY);
}
private Bitmap imageProxytoBitmap(ImageProxy image) {
ByteBuffer buffer =image.getPlanes()[0].getBuffer();
buffer.rewind();
byte[] bytes = new byte[buffer.capacity()];
buffer.get(bytes);
byte[] cloneBytes = bytes.clone();
return BitmapFactory.decodeByteArray(cloneBytes, 0, cloneBytes.length);
}
I want the captured image to exactly same as the preview (unrotated image)
If you open the rotated image in the Google Photo app, are they still rotated? If not, then it means that you are not using the EXIF info.
Basically when a photo is captured, it may or may not be rotated depending on the OEM. When it's not rotated, CameraX saves the rotation info in the TAG_ORIENTATION tag. You will need to read that info and apply it on the Bitmap yourself. Something like:
// Read the exif rotation value from the JPEG byte buffer
ExifInterface exifInterface = new ExifInterface(jpegFilePath);
int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED);
// Decode the JPEG byte buffer into a bitmap
Bitmap bitmap = BitmapFactory.decodeFile(jpegFilePath);
// Rotate the bitmap based on the exif rotation value
Matrix matrix = new Matrix();
switch (orientation) {
case ExifInterface.ORIENTATION_ROTATE_90:
matrix.postRotate(90);
break;
case ExifInterface.ORIENTATION_ROTATE_180:
matrix.postRotate(180);
break;
case ExifInterface.ORIENTATION_ROTATE_270:
matrix.postRotate(270);
break;
}
bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
Code not tested. You might need to swap width/height during rotation.
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>
When i am in landscape, if it's at 0degrees, it captures a good video, but if i turn the phone over 180 degrees, it will record upside down, how can i change this?
When i start recording i do this code:
myMediaRecorder.setPreviewDisplay(mySurfaceHolder.getSurface());
if (rotationInDegreeValue == 90) {
LogService.log(TAG, "set orientation hint : " + 0);
myMediaRecorder.setOrientationHint(0);
} else if (rotationInDegreeValue == 270) {
LogService.log(TAG, "set orientation hint : " + 180);// 180
myMediaRecorder.setOrientationHint(180);
}
myMediaRecorder.prepare();
myMediaRecorder.start();
Then, i have an orientation listener:
orientationListener = new OrientationEventListener(getActivity(), SensorManager.SENSOR_DELAY_UI) {
#Override
public void onOrientationChanged(int orientation) {
LogService.log(TAG, "onOrientationChanged");
if ((myCamera == null)) {
return;
}
Log.d(TAG, "orientation changed : " + orientation);
if (((orientation < 45) || (orientation > 315) || ((orientation < 225) && (orientation > 135))) && !isRecording) {
if (!isAlertShown && !isUserListDisplayed) {
// AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(getActivity());
// alertDialogBuilder.setTitle("Orientation").setMessage("Return to landscape").setCancelable(false);
// orientationAlert = alertDialogBuilder.create();
// orientationAlert.show();
isAlertShown = true;
}
} else {
if (isAlertShown) {
// orientationAlert.hide();
// orientationAlert.dismiss();
isAlertShown = false;
}
}
rotateCamera();
}
};
This is the RotateCamera Function:
private void rotateCamera() {
LogService.log(TAG, "rotateCamera()");
int cameraId = CameraInfo.CAMERA_FACING_BACK;
if (isUsingBackCam) {
cameraId = CameraInfo.CAMERA_FACING_FRONT;
}
android.hardware.Camera.CameraInfo myCameraInfo = new android.hardware.Camera.CameraInfo();
android.hardware.Camera.getCameraInfo(cameraId, myCameraInfo);
Display display;
display = getActivity().getWindow().getWindowManager().getDefaultDisplay();
int rotation = display.getRotation();
int degrees = 0;
Point size = new Point();
display.getSize(size);
switch (rotation) {
case Surface.ROTATION_0:
degrees = 0;
rotationInDegreeValue = 0;
SCREEN_HEIGHT = size.x;
break;
case Surface.ROTATION_90:
degrees = 90;
rotationInDegreeValue = 90;
SCREEN_HEIGHT = size.y;
break;
case Surface.ROTATION_180:
degrees = 180;
rotationInDegreeValue = 180;
break;
case Surface.ROTATION_270:
rotationInDegreeValue = 270;
degrees = 270;
SCREEN_HEIGHT = size.y;
break;
}
int result;
if (myCameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
result = (myCameraInfo.orientation + degrees) % 360;
result = (360 - result) % 360; // compensate the mirror
} else { // back-facing
result = ((myCameraInfo.orientation - degrees) + 360) % 360;
}
if (!isRecording) {
try {
myCamera.setDisplayOrientation(result);
} catch (Exception e) {
LogService.err(TAG, e.getMessage(), e, 1);
}
}
}
What do i have to do, to stop the camera from recording upside down, when i turn the phone 180 degrees. I tried to comment out the orientation listener, but still no luck.
Also, i do not know if it is important of not, but this happens in a fragment. The Activity of the fragment, does not have onconfigurationchanged set.
The setOrientationHint function worked, but i had to append more videos, and when this was happening, the composition matrix of the videos was getting lost.
I have changed in the manifest, from SensorLandscape to Landscape, and this is how i solved this issue.
I'm trying to develop a camera recorder app in portrait mode. When I record a video with the back camera, the video has the properly orientation because it do the right rotations. But, when I try to show the preview with the front camera, in some devices it works fine, but in others the video has a rotation of 180ยบ like HTC Desire. So, how to control this problem to rotate the camera in right position?
UPDATE: I've found this link which explains my problems, and the solutions are to re-encode the video, to develop an playback activity that understands the metadata or to upload the video to the server and rotate it. My best solution will be develop a new playback video in my application. Could someone give me some guidelines? Is necesary to use the ffmpeg library? I've developed a basic playback video, but how can I orientate the video using the metadata?
private boolean initCamera() {
try {
camera = getCameraInstance(selected_camera);
Camera.Parameters camParams = camera.getParameters();
camParams.set( "cam_mode", 1 );
// camParams.set("orientation", "portrait");
checkCameraFlash(camParams);
setAngleCameraRotation();
// camera.setDisplayOrientation(angle_rotation_camera);
setAspectResolutionCamera(camParams);
camera.setParameters(camParams);
video_view.getHolder().setFixedSize(height_video, width_video);
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(height_video, width_video);
video_view.setLayoutParams(lp);
camera.lock();
surface_holder = video_view.getHolder();
surface_holder.addCallback(this);
surface_holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
setPreviewCamera();
} catch(Exception e) {
Log.v("RecordVideo", "Could not initialize the Camera");
return false;
}
return true;
}
private void initCameraRecorder() {
if(media_recorder != null) return;
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
output_file_name = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM) + File.separator + timeStamp + ".mp4";
File outFile = new File(output_file_name);
if(outFile.exists()) {
outFile.delete();
}
try {
camera.stopPreview();
camera.unlock();
media_recorder = new MediaRecorder();
media_recorder.setCamera(camera);
media_recorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
media_recorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
CamcorderProfile profile = CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH);
profile.videoBitRate = 885000; //Medium quality
profile.videoFrameHeight = height_video;
profile.videoFrameWidth = width_video;
media_recorder.setProfile(profile);
media_recorder.setMaxDuration(21000); // limit to 21 seconds
media_recorder.setOrientationHint(setOrientationCameraRecorder());
media_recorder.setPreviewDisplay(surface_holder.getSurface());
media_recorder.setOutputFile(output_file_name);
media_recorder.prepare();
Log.v("RecordVideo", "MediaRecorder initialized");
} catch(Exception e) {
Log.v("RecordVideo", "MediaRecorder failed to initialize");
e.printStackTrace();
}
}
public static Camera getCameraInstance(int camera_id) {
Camera c = null;
try {
c = Camera.open(camera_id); // attempt to get a Camera instance
}
catch (Exception e){
// Camera is not available (in use or does not exist)
}
return c; // returns null if camera is unavailable
}
private void setAspectResolutionCamera(Parameters camParams) {
float aspect_ratio = 1f;
int aspect_width = 2000, aspect_height = 2000;
List<Size> supported_sizes_list = camParams.getSupportedPreviewSizes();
for (int i = 0; i < supported_sizes_list.size(); i++) {
Size size = supported_sizes_list.get(i);
if (Math.abs(VIDEO_ASPECT_RATIO - (float) size.height / size.width) <= aspect_ratio ) {
if (Math.abs(VIDEO_ASPECT_WIDTH - size.height) <= aspect_width) {
if (Math.abs(VIDEO_ASPECT_HEIGHT - size.width) < aspect_height) {
width_video = size.width;
height_video = size.height;
aspect_ratio = Math.abs(VIDEO_ASPECT_RATIO - (float) size.height / size.width);
aspect_width = Math.abs(VIDEO_ASPECT_WIDTH - size.height);
aspect_height = Math.abs(VIDEO_ASPECT_HEIGHT - size.width);
}
}
}
}
}
/** Sets the angle camera rotation. It's the user sees */
private void setAngleCameraRotation() {
Camera.CameraInfo info = new Camera.CameraInfo();
android.hardware.Camera.getCameraInfo(selected_camera, info); // back-facing camera
int rotation = 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;
}
angle_rotation_camera = (info.orientation - degrees) % 360; // back-facing camera rotation
}
I want to set the camera orientation according to the device orientation in Android but nothing seems to be working. I tried to rotate the Surface as well as the camera parameters but the camera preview in portrait mode always comes upside down. I would need to rotate it by 90 degree clockwise for it to be correct. Here is the code I am using right now which works in landscape mode only.
SurfaceHolder.Callback surfaceCallback = new SurfaceHolder.Callback() {
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
camera.stopPreview();
camera.release();
camera = null;
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
initCamera();
}
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(TAG, "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 surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
Camera.Parameters parameters = camera.getParameters();
List<Size> sizes = parameters.getSupportedPreviewSizes();
Size optimalSize = getOptimalPreviewSize(sizes, width, height);
Log.d(TAG, "Surface size is " + width + "w " + height + "h");
Log.d(TAG, "Optimal size is " + optimalSize.width + "w " + optimalSize.height + "h");
parameters.setPreviewSize(optimalSize.width, optimalSize.height);
// parameters.setPreviewSize(width, height);
camera.setParameters(parameters);
camera.startPreview();
}
};
From other member and my problem:
Camera Rotation issue depend on different Devices and certain Version.
Version 1.6: to fix the Rotation Issue, and it is good for most of devices
if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT)
{
p.set("orientation", "portrait");
p.set("rotation",90);
}
if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE)
{
p.set("orientation", "landscape");
p.set("rotation", 90);
}
Version 2.1: depend on kind of devices, for example, Cannt fix the issue with XPeria X10, but it is good for X8, and Mini
Camera.Parameters parameters = camera.getParameters();
parameters.set("orientation", "portrait");
camera.setParameters(parameters);
Version 2.2: not for all devices
camera.setDisplayOrientation(90);
http://code.google.com/p/android/issues/detail?id=1193#c42
From the Javadocs for setDisplayOrientation(int) (Requires API level 9):
public static void setCameraDisplayOrientation(Activity activity,
int cameraId, android.hardware.Camera camera) {
android.hardware.Camera.CameraInfo info =
new android.hardware.Camera.CameraInfo();
android.hardware.Camera.getCameraInfo(cameraId, info);
int rotation = activity.getWindowManager().getDefaultDisplay()
.getRotation();
int degrees = 0;
switch (rotation) {
case Surface.ROTATION_0: degrees = 0; break;
case Surface.ROTATION_90: degrees = 90; break;
case Surface.ROTATION_180: degrees = 180; break;
case Surface.ROTATION_270: degrees = 270; break;
}
int result;
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
result = (info.orientation + degrees) % 360;
result = (360 - result) % 360; // compensate the mirror
} else { // back-facing
result = (info.orientation - degrees + 360) % 360;
}
camera.setDisplayOrientation(result);
}
This solution will work for all versions of Android. You can use reflection in Java to make it work for all Android devices:
Basically you should create a reflection wrapper to call the Android 2.2 setDisplayOrientation, instead of calling the specific method.
The method:
protected void setDisplayOrientation(Camera camera, int angle){
Method downPolymorphic;
try
{
downPolymorphic = camera.getClass().getMethod("setDisplayOrientation", new Class[] { int.class });
if (downPolymorphic != null)
downPolymorphic.invoke(camera, new Object[] { angle });
}
catch (Exception e1)
{
}
}
And instead of using camera.setDisplayOrientation(x) use setDisplayOrientation(camera, x) :
if (Integer.parseInt(Build.VERSION.SDK) >= 8)
setDisplayOrientation(mCamera, 90);
else
{
if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT)
{
p.set("orientation", "portrait");
p.set("rotation", 90);
}
if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE)
{
p.set("orientation", "landscape");
p.set("rotation", 90);
}
}
I faced the issue when i was using ZBar for scanning in tabs.
Camera orientation issue. Using below code i was able to resolve issue.
This is not the whole code snippet, Please take only help from this.
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
if (isPreviewRunning) {
mCamera.stopPreview();
}
setCameraDisplayOrientation(mCamera);
previewCamera();
}
public void previewCamera() {
try {
// Hard code camera surface rotation 90 degs to match Activity view
// in portrait
mCamera.setPreviewDisplay(mHolder);
mCamera.setPreviewCallback(previewCallback);
mCamera.startPreview();
mCamera.autoFocus(autoFocusCallback);
isPreviewRunning = true;
} catch (Exception e) {
Log.d("DBG", "Error starting camera preview: " + e.getMessage());
}
}
public void setCameraDisplayOrientation(android.hardware.Camera camera) {
Camera.Parameters parameters = camera.getParameters();
android.hardware.Camera.CameraInfo camInfo =
new android.hardware.Camera.CameraInfo();
android.hardware.Camera.getCameraInfo(getBackFacingCameraId(), camInfo);
Display display = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
int rotation = display.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 (camInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
result = (camInfo.orientation + degrees) % 360;
result = (360 - result) % 360; // compensate the mirror
} else { // back-facing
result = (camInfo.orientation - degrees + 360) % 360;
}
camera.setDisplayOrientation(result);
}
private int getBackFacingCameraId() {
int cameraId = -1;
// Search for the front facing camera
int numberOfCameras = Camera.getNumberOfCameras();
for (int i = 0; i < numberOfCameras; i++) {
Camera.CameraInfo info = new Camera.CameraInfo();
Camera.getCameraInfo(i, info);
if (info.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {
cameraId = i;
break;
}
}
return cameraId;
}
I finally fixed this using the Google's camera app. It gets the phone's orientation by using a sensor and then sets the EXIF tag appropriately. The JPEG which comes out of the camera is not oriented automatically.
Also, the camera preview works properly only in the landscape mode. If you need your activity layout to be oriented in portrait, you will have to do it manually using the value from the orientation sensor.
This problem was solved a long time ago but I encountered some difficulties to put all pieces together so here is my final solution, I hope this will help others :
public void startPreview() {
try {
Log.i(TAG, "starting preview: " + started);
// ....
Camera.CameraInfo camInfo = new Camera.CameraInfo();
Camera.getCameraInfo(cameraIndex, camInfo);
int cameraRotationOffset = camInfo.orientation;
// ...
Camera.Parameters parameters = camera.getParameters();
List<Camera.Size> previewSizes = parameters.getSupportedPreviewSizes();
Camera.Size previewSize = null;
float closestRatio = Float.MAX_VALUE;
int targetPreviewWidth = isLandscape() ? getWidth() : getHeight();
int targetPreviewHeight = isLandscape() ? getHeight() : getWidth();
float targetRatio = targetPreviewWidth / (float) targetPreviewHeight;
Log.v(TAG, "target size: " + targetPreviewWidth + " / " + targetPreviewHeight + " ratio:" + targetRatio);
for (Camera.Size candidateSize : previewSizes) {
float whRatio = candidateSize.width / (float) candidateSize.height;
if (previewSize == null || Math.abs(targetRatio - whRatio) < Math.abs(targetRatio - closestRatio)) {
closestRatio = whRatio;
previewSize = candidateSize;
}
}
int rotation = getWindowManager().getDefaultDisplay().getRotation();
int degrees = 0;
switch (rotation) {
case Surface.ROTATION_0:
degrees = 0;
break; // Natural orientation
case Surface.ROTATION_90:
degrees = 90;
break; // Landscape left
case Surface.ROTATION_180:
degrees = 180;
break;// Upside down
case Surface.ROTATION_270:
degrees = 270;
break;// Landscape right
}
int displayRotation;
if (isFrontFacingCam) {
displayRotation = (cameraRotationOffset + degrees) % 360;
displayRotation = (360 - displayRotation) % 360; // compensate
// the
// mirror
} else { // back-facing
displayRotation = (cameraRotationOffset - degrees + 360) % 360;
}
Log.v(TAG, "rotation cam / phone = displayRotation: " + cameraRotationOffset + " / " + degrees + " = "
+ displayRotation);
this.camera.setDisplayOrientation(displayRotation);
int rotate;
if (isFrontFacingCam) {
rotate = (360 + cameraRotationOffset + degrees) % 360;
} else {
rotate = (360 + cameraRotationOffset - degrees) % 360;
}
Log.v(TAG, "screenshot rotation: " + cameraRotationOffset + " / " + degrees + " = " + rotate);
Log.v(TAG, "preview size: " + previewSize.width + " / " + previewSize.height);
parameters.setPreviewSize(previewSize.width, previewSize.height);
parameters.setRotation(rotate);
camera.setParameters(parameters);
camera.setPreviewDisplay(mHolder);
camera.startPreview();
Log.d(TAG, "preview started");
started = true;
} catch (IOException e) {
Log.d(TAG, "Error setting camera preview: " + e.getMessage());
}
}
check out this solution
public static void setCameraDisplayOrientation(Activity activity,
int cameraId, android.hardware.Camera camera) {
android.hardware.Camera.CameraInfo info =
new android.hardware.Camera.CameraInfo();
android.hardware.Camera.getCameraInfo(cameraId, info);
int rotation = activity.getWindowManager().getDefaultDisplay()
.getRotation();
int degrees = 0;
switch (rotation) {
case Surface.ROTATION_0: degrees = 0; break;
case Surface.ROTATION_90: degrees = 90; break;
case Surface.ROTATION_180: degrees = 180; break;
case Surface.ROTATION_270: degrees = 270; break;
}
int result;
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
result = (info.orientation + degrees) % 360;
result = (360 - result) % 360; // compensate the mirror
} else { // back-facing
result = (info.orientation - degrees + 360) % 360;
}
camera.setDisplayOrientation(result);
}