Camera records upside down sometimes - android

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.

Related

Android landscape screen 180° rotation

I need to know when my Android device screen is rotated from one landscape to another (rotation_90 to rotation_270).
In my Android service, I reimplemented onConfigurationChanged(Configuration newConfig) to be aware of the rotation of the device. But this method is only called by the system if the device is rotated from ORIENTATION_PORTRAIT to ORIENTATION_LANDSCAPE, and not if it is rotated from ORIENTATION_LANDSCAPE (90°) to the other ORIENTATION_LANDSCAPE (270°) !!
How can I be called in this case?
Thanks.
You can enable an OrientationEventListener to your activity.
OrientationEventListener mOrientationListener = new OrientationEventListener(this, SensorManager.SENSOR_DELAY_NORMAL) {
#Override
public void onOrientationChanged(int orientation) {
Log.v(TAG, "Orientation changed to " + orientation);
if (orientation == OrientationEventListener.ORIENTATION_UNKNOWN) {
return;
}
int degrees = -1;
if (orientation < 45 || orientation > 315) {
Log.i(TAG, "Portrait");
} else if (orientation < 135) {
degrees = 90;
Log.i(TAG, "Landscape"); // This can be reverse landscape
} else if (orientation < 225) {
degrees = 180;
Log.i(TAG, "Reverse Portrait");
} else {
degrees = 270;
Log.i(TAG, "Reverse Landscape"); // This can be landscape
}
}
};
if (mOrientationListener.canDetectOrientation() == true) {
Log.v(TAG, "Can detect orientation");
mOrientationListener.enable();
} else {
Log.v(TAG, "Cannot detect orientation");
mOrientationListener.disable();
}
You can save previous orientation as int member variable by using this code:
int oldRotation = getWindowManager().getDefaultDisplay().getRotation();
And then check if the device was rotated from one landscape mode to another.
if(rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270) {
int newRotation = getWindowManager().getDefaultDisplay().getRotation();
if(newRotation != oldRotation) {
// rotation from 90 to 270, or from 270 to 90
}
oldRotation = newRotation;
}

Rotating device changing image orientation ?

This is my camera Activity which is in Portrait mode. I am rotating the device and the camera is rotating with it.
This is my second Activity where my image is displaying on the ImageView upside down, received from PictureCallback(). I already tried ExifInterface but its returning orientation 0.
I had a similar Problem. Orientation 0 in ExifInterface means undefined. If that occurs i ask the MediaStore and in my case it always returns the correct orientation if ExifInterface returns 0.
String[] cols = { MediaStore.Images.Media.ORIENTATION };
Cursor cur = context.getContentResolver().query(uri, cols, null, null, null);
int orientation = 0;
if (cur != null && cur.moveToFirst()) {
orientation = cur.getInt(cur.getColumnIndex(MediaStore.Images.Media.ORIENTATION));
}
Setting for camera:
public static void setCameraDisplayOrientation(Activity activity,
int cameraId, android.hardware.Camera camera) {
try {
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);
} catch (Exception e) {
// TODO: handle exception
}
}
And use ExifInterface when display result

android camera setDisplayOrientation(90) fails in different devices

I have a problem with my camera app.Camera activity is in portrait mode.So i gave
camera.setDisplayOrientation(90);
As per different posts in SO, this will work fine in portrait mode.
but it never works fine with different devices.The issue is, preview rotated 90 degree left or right.In Htc it's ok. But with galaxy series it's not working.
Can anyone help me ?
Set orientation and preview in following different ways, use as requirement :
First Ways :
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height)
{
if (isPreviewRunning)
{
mCamera.stopPreview();
}
Parameters parameters = mCamera.getParameters();
Display display = ((WindowManager)getSystemService(WINDOW_SERVICE)).getDefaultDisplay();
if(display.getRotation() == Surface.ROTATION_0)
{
parameters.setPreviewSize(height, width);
mCamera.setDisplayOrientation(90);
}
if(display.getRotation() == Surface.ROTATION_90)
{
parameters.setPreviewSize(width, height);
}
if(display.getRotation() == Surface.ROTATION_180)
{
parameters.setPreviewSize(height, width);
}
if(display.getRotation() == Surface.ROTATION_270)
{
parameters.setPreviewSize(width, height);
mCamera.setDisplayOrientation(180);
}
mCamera.setParameters(parameters);
previewCamera();
}
and
public void previewCamera()
{
try
{
mCamera.setPreviewDisplay(mSurfaceHolder);
mCamera.startPreview();
isPreviewRunning = true;
}
catch(Exception e)
{
Log.d(APP_CLASS, "Cannot start preview", e);
}
}
Second ways :
private Camera mCamera;
private OrientationEventListener mOrientationEventListener;
private int mOrientation = -1;
private static final int ORIENTATION_PORTRAIT_NORMAL = 1;
private static final int ORIENTATION_PORTRAIT_INVERTED = 2;
private static final int ORIENTATION_LANDSCAPE_NORMAL = 3;
private static final int ORIENTATION_LANDSCAPE_INVERTED = 4;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// force Landscape layout
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR | ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
/*
Your other initialization code here
*/
}
#Override
protected void onResume() {
super.onResume();
if (mOrientationEventListener == null) {
mOrientationEventListener = new OrientationEventListener(this, SensorManager.SENSOR_DELAY_NORMAL) {
#Override
public void onOrientationChanged(int orientation) {
// determine our orientation based on sensor response
int lastOrientation = mOrientation;
if (orientation >= 315 || orientation < 45) {
if (mOrientation != ORIENTATION_PORTRAIT_NORMAL) {
mOrientation = ORIENTATION_PORTRAIT_NORMAL;
}
}
else if (orientation < 315 && orientation >= 225) {
if (mOrientation != ORIENTATION_LANDSCAPE_NORMAL) {
mOrientation = ORIENTATION_LANDSCAPE_NORMAL;
}
}
else if (orientation < 225 && orientation >= 135) {
if (mOrientation != ORIENTATION_PORTRAIT_INVERTED) {
mOrientation = ORIENTATION_PORTRAIT_INVERTED;
}
}
else { // orientation <135 && orientation > 45
if (mOrientation != ORIENTATION_LANDSCAPE_INVERTED) {
mOrientation = ORIENTATION_LANDSCAPE_INVERTED;
}
}
if (lastOrientation != mOrientation) {
changeRotation(mOrientation, lastOrientation);
}
}
};
}
if (mOrientationEventListener.canDetectOrientation()) {
mOrientationEventListener.enable();
}
}
#Override protected void onPause() {
super.onPause();
mOrientationEventListener.disable();
}
/**
* Performs required action to accommodate new orientation
* #param orientation
* #param lastOrientation
*/
private void changeRotation(int orientation, int lastOrientation) {
switch (orientation) {
case ORIENTATION_PORTRAIT_NORMAL:
mSnapButton.setImageDrawable(getRotatedImage(android.R.drawable.ic_menu_camera, 270));
mBackButton.setImageDrawable(getRotatedImage(android.R.drawable.ic_menu_revert, 270));
Log.v("CameraActivity", "Orientation = 90");
break;
case ORIENTATION_LANDSCAPE_NORMAL:
mSnapButton.setImageResource(android.R.drawable.ic_menu_camera);
mBackButton.setImageResource(android.R.drawable.ic_menu_revert);
Log.v("CameraActivity", "Orientation = 0");
break;
case ORIENTATION_PORTRAIT_INVERTED:
mSnapButton.setImageDrawable(getRotatedImage(android.R.drawable.ic_menu_camera, 90));
mBackButton.setImageDrawable(getRotatedImage(android.R.drawable.ic_menu_revert, 90));
Log.v("CameraActivity", "Orientation = 270");
break;
case ORIENTATION_LANDSCAPE_INVERTED:
mSnapButton.setImageDrawable(getRotatedImage(android.R.drawable.ic_menu_camera, 180));
mBackButton.setImageDrawable(getRotatedImage(android.R.drawable.ic_menu_revert, 180));
Log.v("CameraActivity", "Orientation = 180");
break;
}
}
/**
* Rotates given Drawable
* #param drawableId Drawable Id to rotate
* #param degrees Rotate drawable by Degrees
* #return Rotated Drawable
*/
private Drawable getRotatedImage(int drawableId, int degrees) {
Bitmap original = BitmapFactory.decodeResource(getResources(), drawableId);
Matrix matrix = new Matrix();
matrix.postRotate(degrees);
Bitmap rotated = Bitmap.createBitmap(original, 0, 0, original.getWidth(), original.getHeight(), matrix, true);
return new BitmapDrawable(rotated);
}
And then in your PictureCallback set metadata to indicate rotation level:
private Camera.PictureCallback mJpegCallback = new Camera.PictureCallback() {
#Override
public void onPictureTaken(byte[] data, Camera camera) {
try {
// Populate image metadata
ContentValues image = new ContentValues();
// additional picture metadata
image.put(Media.DISPLAY_NAME, [picture name]);
image.put(Media.MIME_TYPE, "image/jpg");
image.put(Media.TITLE, [picture title]);
image.put(Media.DESCRIPTION, [picture description]);
image.put(Media.DATE_ADDED, [some time]);
image.put(Media.DATE_TAKEN, [some time]);
image.put(Media.DATE_MODIFIED, [some time]);
// do not rotate image, just put rotation info in
switch (mOrientation) {
case ORIENTATION_PORTRAIT_NORMAL:
image.put(Media.ORIENTATION, 90);
break;
case ORIENTATION_LANDSCAPE_NORMAL:
image.put(Media.ORIENTATION, 0);
break;
case ORIENTATION_PORTRAIT_INVERTED:
image.put(Media.ORIENTATION, 270);
break;
case ORIENTATION_LANDSCAPE_INVERTED:
image.put(Media.ORIENTATION, 180);
break;
}
// store the picture
Uri uri = getContentResolver().insert(
Media.EXTERNAL_CONTENT_URI, image);
try {
Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0,
data.length);
OutputStream out = getContentResolver().openOutputStream(
uri);
boolean success = bitmap.compress(
Bitmap.CompressFormat.JPEG, 75, out);
out.close();
if (!success) {
finish(); // image output failed without any error,
// silently finish
}
Now when landscape based devices are appearing an additional check for it is required in OrientationEventListener.
Display display = ((WindowManager)getSystemService(WINDOW_SERVICE)).getDefaultDisplay();
if (display.getOrientation() == Surface.ROTATION_0) {
// landscape oriented devices
} else {
// portrait oriented device
}
Full code (a bit wasteful by LC, but easily demonstrates the approach)
#Override
public void onOrientationChanged(int orientation) {
// determine our orientation based on sensor response
int lastOrientation = mOrientation;
Display display = ((WindowManager)getSystemService(WINDOW_SERVICE)).getDefaultDisplay();
if (display.getOrientation() == Surface.ROTATION_0) { // landscape oriented devices
if (orientation >= 315 || orientation < 45) {
if (mOrientation != ORIENTATION_LANDSCAPE_NORMAL) {
mOrientation = ORIENTATION_LANDSCAPE_NORMAL;
}
} else if (orientation < 315 && orientation >= 225) {
if (mOrientation != ORIENTATION_PORTRAIT_INVERTED) {
mOrientation = ORIENTATION_PORTRAIT_INVERTED;
}
} else if (orientation < 225 && orientation >= 135) {
if (mOrientation != ORIENTATION_LANDSCAPE_INVERTED) {
mOrientation = ORIENTATION_LANDSCAPE_INVERTED;
}
} else if (orientation <135 && orientation > 45) {
if (mOrientation != ORIENTATION_PORTRAIT_NORMAL) {
mOrientation = ORIENTATION_PORTRAIT_NORMAL;
}
}
} else { // portrait oriented devices
if (orientation >= 315 || orientation < 45) {
if (mOrientation != ORIENTATION_PORTRAIT_NORMAL) {
mOrientation = ORIENTATION_PORTRAIT_NORMAL;
}
} else if (orientation < 315 && orientation >= 225) {
if (mOrientation != ORIENTATION_LANDSCAPE_NORMAL) {
mOrientation = ORIENTATION_LANDSCAPE_NORMAL;
}
} else if (orientation < 225 && orientation >= 135) {
if (mOrientation != ORIENTATION_LANDSCAPE_INVERTED) {
mOrientation = ORIENTATION_LANDSCAPE_INVERTED;
}
} else if (orientation <135 && orientation > 45) {
if (mOrientation != ORIENTATION_PORTRAIT_NORMAL) {
mOrientation = ORIENTATION_PORTRAIT_NORMAL;
}
}
} else { // portrait oriented devices
if (orientation >= 315 || orientation < 45) {
if (mOrientation != ORIENTATION_PORTRAIT_NORMAL) {
mOrientation = ORIENTATION_PORTRAIT_NORMAL;
}
} else if (orientation < 315 && orientation >= 225) {
if (mOrientation != ORIENTATION_LANDSCAPE_NORMAL) {
mOrientation = ORIENTATION_LANDSCAPE_NORMAL;
}
} else if (orientation < 225 && orientation >= 135) {
if (mOrientation != ORIENTATION_PORTRAIT_INVERTED) {
mOrientation = ORIENTATION_PORTRAIT_INVERTED;
}
} else if (orientation <135 && orientation > 45) {
if (mOrientation != ORIENTATION_LANDSCAPE_INVERTED) {
mOrientation = ORIENTATION_LANDSCAPE_INVERTED;
}
}
}
if (lastOrientation != mOrientation) {
changeRotation(mOrientation, lastOrientation);
}
}
Third ways :
private Bitmap adjustImageOrientation(Bitmap image) {
ExifInterface exif;
try {
exif = new ExifInterface(picturePath);
int exifOrientation = exif.getAttributeInt(
ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_NORMAL);
int rotate = 0;
switch (exifOrientation) {
case ExifInterface.ORIENTATION_ROTATE_90:
rotate = 90;
break;
case ExifInterface.ORIENTATION_ROTATE_180:
rotate = 180;
break;
case ExifInterface.ORIENTATION_ROTATE_270:
rotate = 270;
break;
}
if (rotate != 0) {
int w = image.getWidth();
int h = image.getHeight();
// Setting pre rotate
Matrix mtx = new Matrix();
mtx.preRotate(rotate);
// Rotating Bitmap & convert to ARGB_8888, required by tess
image = Bitmap.createBitmap(image, 0, 0, w, h, mtx, false);
}
} catch (IOException e) {
return null;
}
return image.copy(Bitmap.Config.ARGB_8888, true);
}
I gave this answer to a similar question, but as you say it was on an HTC device. I would recommend that you add breakpoints to the rotation code and examine the variables while physically rotating the device - this may help identify whats different with the Galaxy models.
Check if the "Auto-rotate Screen" option is checked in the phone Settings (Settings > Dislay or Screen - depends on the android version).
Just posting a new solution that worked for me.
Basically you can get the orientation value from the Camera.CameraInfo. This will tell the degrees you need to use on setDisplayOrientation so the image is properly displayed. When using the back facing camera you can simply use setDisplayOrientation with the value retrieved, But when using the front facing camera you need to tweak it a bit as the Android system will flip the image so it appears like a mirror.
Below code worked for me and tested on 4 different devices including a Nexus6 and Galaxy.
Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
int cameraCount = Camera.getNumberOfCameras();
int camIdx = 0; // DO your logic to get front or back camera...or loop through all avaialable.
Camera.getCameraInfo(camIdx, cameraInfo);
try {
mCamera = Camera.open(camIdx);
// If using back camera then simply rotate what CameraInfo tells you.
if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK)
mCamera.setDisplayOrientation(cameraInfo.orientation);
else
// If using front camera note that image might be flipped to give users the impresion the are looking at a mirror.
mCamera.setDisplayOrientation( (360 - cameraInfo.orientation) % 360);
} catch (Exception e) {
e.printStackTrace();
}

android camera surfaceview orientation

Ok so I have a class that extends SurfaceView and overrides
surfaceChanged - just calls startPreview
surfaceCreated - opens camera, edits params *, sets surfaceHolder
surfaceDestroyed - calls stopPreview, release camera
this all work great because when the orientation is Portrait:
from surfaceCreated *
m_camera = Camera.open();
Camera.Parameters p = m_camera.getParameters();
if (getResources().getConfiguration().orientation !=
Configuration.ORIENTATION_LANDSCAPE)
{
p.set("orientation", "portrait");
// CameraApi is a wrapper to check for backwards compatibility
if (CameraApi.isSetRotationSupported())
{
CameraApi.setRotation(p, 90);
}
}
However, everytime the orientation changes it calls Camera.open()... which as you may know is quite an expensive operation, causing the transitions to be not so smooth.
When i force the orientation to landscape, the preview is great. Create only gets called once which works because the preview is in landscape the camera is always what the user sees. However, I need a way to set the orientation of the actual picture taken when in portrait. When I force landscape though, the surface never gets recreated and the parameters are never set when the camera is held in portrait.
So how can I do one of the following (exclusively)?
Hold onto m_camera between onDestroy and onCreate when orientation changes so that the transition is smooth
Force landscape and detect orientation changes another way... rotating the final snaped picture if held in portrait.
Also, if I am off base can someone point me in a better direction? Thank you.
The way I implemented it:
private Camera mCamera;
private OrientationEventListener mOrientationEventListener;
private int mOrientation = -1;
private static final int ORIENTATION_PORTRAIT_NORMAL = 1;
private static final int ORIENTATION_PORTRAIT_INVERTED = 2;
private static final int ORIENTATION_LANDSCAPE_NORMAL = 3;
private static final int ORIENTATION_LANDSCAPE_INVERTED = 4;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// force Landscape layout
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR | ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
/*
Your other initialization code here
*/
}
#Override
protected void onResume() {
super.onResume();
if (mOrientationEventListener == null) {
mOrientationEventListener = new OrientationEventListener(this, SensorManager.SENSOR_DELAY_NORMAL) {
#Override
public void onOrientationChanged(int orientation) {
// determine our orientation based on sensor response
int lastOrientation = mOrientation;
if (orientation >= 315 || orientation < 45) {
if (mOrientation != ORIENTATION_PORTRAIT_NORMAL) {
mOrientation = ORIENTATION_PORTRAIT_NORMAL;
}
}
else if (orientation < 315 && orientation >= 225) {
if (mOrientation != ORIENTATION_LANDSCAPE_NORMAL) {
mOrientation = ORIENTATION_LANDSCAPE_NORMAL;
}
}
else if (orientation < 225 && orientation >= 135) {
if (mOrientation != ORIENTATION_PORTRAIT_INVERTED) {
mOrientation = ORIENTATION_PORTRAIT_INVERTED;
}
}
else { // orientation <135 && orientation > 45
if (mOrientation != ORIENTATION_LANDSCAPE_INVERTED) {
mOrientation = ORIENTATION_LANDSCAPE_INVERTED;
}
}
if (lastOrientation != mOrientation) {
changeRotation(mOrientation, lastOrientation);
}
}
};
}
if (mOrientationEventListener.canDetectOrientation()) {
mOrientationEventListener.enable();
}
}
#Override protected void onPause() {
super.onPause();
mOrientationEventListener.disable();
}
/**
* Performs required action to accommodate new orientation
* #param orientation
* #param lastOrientation
*/
private void changeRotation(int orientation, int lastOrientation) {
switch (orientation) {
case ORIENTATION_PORTRAIT_NORMAL:
mSnapButton.setImageDrawable(getRotatedImage(android.R.drawable.ic_menu_camera, 270));
mBackButton.setImageDrawable(getRotatedImage(android.R.drawable.ic_menu_revert, 270));
Log.v("CameraActivity", "Orientation = 90");
break;
case ORIENTATION_LANDSCAPE_NORMAL:
mSnapButton.setImageResource(android.R.drawable.ic_menu_camera);
mBackButton.setImageResource(android.R.drawable.ic_menu_revert);
Log.v("CameraActivity", "Orientation = 0");
break;
case ORIENTATION_PORTRAIT_INVERTED:
mSnapButton.setImageDrawable(getRotatedImage(android.R.drawable.ic_menu_camera, 90));
mBackButton.setImageDrawable(getRotatedImage(android.R.drawable.ic_menu_revert, 90));
Log.v("CameraActivity", "Orientation = 270");
break;
case ORIENTATION_LANDSCAPE_INVERTED:
mSnapButton.setImageDrawable(getRotatedImage(android.R.drawable.ic_menu_camera, 180));
mBackButton.setImageDrawable(getRotatedImage(android.R.drawable.ic_menu_revert, 180));
Log.v("CameraActivity", "Orientation = 180");
break;
}
}
/**
* Rotates given Drawable
* #param drawableId Drawable Id to rotate
* #param degrees Rotate drawable by Degrees
* #return Rotated Drawable
*/
private Drawable getRotatedImage(int drawableId, int degrees) {
Bitmap original = BitmapFactory.decodeResource(getResources(), drawableId);
Matrix matrix = new Matrix();
matrix.postRotate(degrees);
Bitmap rotated = Bitmap.createBitmap(original, 0, 0, original.getWidth(), original.getHeight(), matrix, true);
return new BitmapDrawable(rotated);
}
And then in your PictureCallback set metadata to indicate rotation level:
private Camera.PictureCallback mJpegCallback = new Camera.PictureCallback() {
#Override
public void onPictureTaken(byte[] data, Camera camera) {
try {
// Populate image metadata
ContentValues image = new ContentValues();
// additional picture metadata
image.put(Media.DISPLAY_NAME, [picture name]);
image.put(Media.MIME_TYPE, "image/jpg");
image.put(Media.TITLE, [picture title]);
image.put(Media.DESCRIPTION, [picture description]);
image.put(Media.DATE_ADDED, [some time]);
image.put(Media.DATE_TAKEN, [some time]);
image.put(Media.DATE_MODIFIED, [some time]);
// do not rotate image, just put rotation info in
switch (mOrientation) {
case ORIENTATION_PORTRAIT_NORMAL:
image.put(Media.ORIENTATION, 90);
break;
case ORIENTATION_LANDSCAPE_NORMAL:
image.put(Media.ORIENTATION, 0);
break;
case ORIENTATION_PORTRAIT_INVERTED:
image.put(Media.ORIENTATION, 270);
break;
case ORIENTATION_LANDSCAPE_INVERTED:
image.put(Media.ORIENTATION, 180);
break;
}
// store the picture
Uri uri = getContentResolver().insert(
Media.EXTERNAL_CONTENT_URI, image);
try {
Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0,
data.length);
OutputStream out = getContentResolver().openOutputStream(
uri);
boolean success = bitmap.compress(
Bitmap.CompressFormat.JPEG, 75, out);
out.close();
if (!success) {
finish(); // image output failed without any error,
// silently finish
}
} catch (Exception e) {
e.printStackTrace();
// handle exceptions
}
mResultIntent = new Intent();
mResultIntent.setData(uri);
} catch (Exception e) {
e.printStackTrace();
}
finish();
}
};
I hope it helps.
UPDATE Now when landscape based devices are appearing an additional check for it is required in OrientationEventListener.
Display display = ((WindowManager)getSystemService(WINDOW_SERVICE)).getDefaultDisplay();
if (display.getOrientation() == Surface.ROTATION_0) {
// landscape oriented devices
} else {
// portrait oriented device
}
Full code (a bit wasteful by LC, but easily demonstrates the approach)
#Override
public void onOrientationChanged(int orientation) {
// determine our orientation based on sensor response
int lastOrientation = mOrientation;
Display display = ((WindowManager)getSystemService(WINDOW_SERVICE)).getDefaultDisplay();
if (display.getOrientation() == Surface.ROTATION_0) { // landscape oriented devices
if (orientation >= 315 || orientation < 45) {
if (mOrientation != ORIENTATION_LANDSCAPE_NORMAL) {
mOrientation = ORIENTATION_LANDSCAPE_NORMAL;
}
} else if (orientation < 315 && orientation >= 225) {
if (mOrientation != ORIENTATION_PORTRAIT_INVERTED) {
mOrientation = ORIENTATION_PORTRAIT_INVERTED;
}
} else if (orientation < 225 && orientation >= 135) {
if (mOrientation != ORIENTATION_LANDSCAPE_INVERTED) {
mOrientation = ORIENTATION_LANDSCAPE_INVERTED;
}
} else if (orientation <135 && orientation > 45) {
if (mOrientation != ORIENTATION_PORTRAIT_NORMAL) {
mOrientation = ORIENTATION_PORTRAIT_NORMAL;
}
}
} else { // portrait oriented devices
if (orientation >= 315 || orientation < 45) {
if (mOrientation != ORIENTATION_PORTRAIT_NORMAL) {
mOrientation = ORIENTATION_PORTRAIT_NORMAL;
}
} else if (orientation < 315 && orientation >= 225) {
if (mOrientation != ORIENTATION_LANDSCAPE_NORMAL) {
mOrientation = ORIENTATION_LANDSCAPE_NORMAL;
}
} else if (orientation < 225 && orientation >= 135) {
if (mOrientation != ORIENTATION_PORTRAIT_INVERTED) {
mOrientation = ORIENTATION_PORTRAIT_INVERTED;
}
} else if (orientation <135 && orientation > 45) {
if (mOrientation != ORIENTATION_LANDSCAPE_INVERTED) {
mOrientation = ORIENTATION_LANDSCAPE_INVERTED;
}
}
}
if (lastOrientation != mOrientation) {
changeRotation(mOrientation, lastOrientation);
}
}
Have you considered using the standard method thats provided in the API doc, which you can call on surfaceChanged? You could store the degrees in a global variable to later use when saving the picture. Also could do a simple null checker on your camera variable, so you don't create it again in surfaceCreated.
public void setCameraDisplayOrientation()
{
if (mCamera == null)
{
Log.d(TAG,"setCameraDisplayOrientation - camera null");
return;
}
Camera.CameraInfo info = new Camera.CameraInfo();
Camera.getCameraInfo(CAM_ID, info);
WindowManager winManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
int rotation = winManager.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;
}
mCamera.setDisplayOrientation(result);
}
As you've seen from the other answers, this code gets very complicated. You may want to investigate using a library to help you provide this feature, for example, CWAC-Camera supports OS 2.3 and up (hopefully you can drop OS 2.1 and OS 2.2 support now):
https://github.com/commonsguy/cwac-camera
CWAC-Camera supports locking the camera preview to landscape, and will auto-rotate images into the correction orientation for you. Browse the project issues if you want a taste of all the device specific problems that need to be solved, which IMO are more reasons for trying to use a library instead of maintaining all this code and testing yourself.

How to set Android camera orientation properly?

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

Categories

Resources