Reputation: 6642
Also an issue when rotating from upside down portrait to one of the landscape orientations and back from there to the upside down portrait orientation.I have tested these cases on the default Camera application and it does not seem to have any of these problems.
I have a custom camera which when rotated by 180 degrees say from one landscape to another shows an inverted preview.I have used an OrientationEventListener in my code like this.
mCamera.setDisplayOrientation
is called in setDisplayOrientation
,I am rounding the orientation to 0,90,180,270 using roundOrientation(orientation,orientationHistory)
and I get the natural display rotation using getDisplayRotation
.I instantiate the listener in initializeFirstTime
and enable it initializeFirstTime
and if resuming from a paused state from initializeSecondTime
.I disable the listener in onPause
.I call initializeFirstTime
or initializeSecondTime
in onResume
.
:
class MyOrientationEventListener extends OrientationEventListener
{
@Override
public void onOrientationChanged(int orientation)
{
if(orientation==ORIENTATION_UNKNOWN)
return;
mOrientation=roundOrientation(orientation,0);
int orientationCompensation=getDisplayRotation(CameraActivity.this);
if(mOrientationCompensation!=orientationCompensation)
mOrientationCompensation=orientationCompensation;
}
public MyOrientationEventListener(Context context) {
super(context);
}
private int roundOrientation(int orientation, int orientationHistory) {
boolean changeOrientation = false;
final int ORIENTATION_HYSTERESIS=5;//a tolerance value above the limit
if (orientationHistory == OrientationEventListener.ORIENTATION_UNKNOWN) {
changeOrientation = true;
} else {
int dist = Math.abs(orientation - orientationHistory);
dist = Math.min(dist, 360 - dist);
changeOrientation = (dist >= 45 +ORIENTATION_HYSTERESIS );
}
if (changeOrientation) {
return ((orientation + 45) / 90 * 90) % 360;
}
return orientationHistory;
}
}
private void setDisplayOrientation()
{
mDisplayRotation=getDisplayRotation(this);
mDisplayOrientation=getDisplayOrientation(mDisplayRotation, CameraInfo.CAMERA_FACING_BACK);
mCamera.setDisplayOrientation(mDisplayOrientation);
}
private int getDisplayOrientation(int degrees, int cameraId) {
// See android.hardware.Camera.setDisplayOrientation for
// documentation.
Camera.CameraInfo info = new Camera.CameraInfo();
Camera.getCameraInfo(cameraId, info);
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;
}
private int getDisplayRotation(Activity activity) {
int rotation = activity.getWindowManager().getDefaultDisplay()
.getRotation();
switch (rotation) {
case Surface.ROTATION_0: return 0;
case Surface.ROTATION_90: return 90;
case Surface.ROTATION_180: return 180;
case Surface.ROTATION_270: return 270;
}
return 0;
}
private MyOrientationEventListener mOrientationListener;
private int mOrientation=OrientationEventListener.ORIENTATION_UNKNOWN;
private int mOrientationCompensation=0;
private int mDisplayRotation;
private int mDisplayOrientation;
These two methods are used to activate the OrientationEventListener:
private void initializeFirstTime()
{
//checking if previously initialized and setting camera parameters
mOrientationListener=new MyOrientationEventListener(CameraActivity.this);
mOrientationListener.enable();
mFirstTimeInitialized=true;
}
private void initializeSecondTime()
{
mOrientationListener.enable();
}
The Camera Preview is started,stopeed and restarted like this:
private void startPreview() throws CameraHardwareException
{
//check if called before onPause or after onResume,
//open the Camera if null,stop the preview,set the parameters
setDisplayOrientation();
try
{
Log.d(TAG, "Trying to start the preview");
mCamera.startPreview();
}
catch(Throwable ex)
{
closeCamera();
throw new RuntimeException("Could not start camera preview");
}
mPreviewing=true;
}
The Activity lifecycle methods have been implemented like this:
@Override
public void onResume()
{
super.onResume();
mPausing=false;
//check condition for SurfaceHolder
//probsbly happens if camera has paused and is resuming
if(mStatus==PREVIEW_STOPPED)
{
try {
startPreview();
} catch (CameraHardwareException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(surfaceHolder!=null)
{
if(!mFirstTimeInitialized)
initializeFirstTime();
else
initializeSecondTime();
}
keepScreenOnAwhile();
if(mStatus==CAMERA_IDLE)
{
mResumeTime=SystemClock.uptimeMillis();
handler.sendEmptyMessageDelayed(CHECK_DISPLAY_ROTATION, 100);
}
}
@Override
public void onPause()
{
mPausing=true;
stopPreview();
closeCamera();
resetScreenOn();
if(mFirstTimeInitialized)
mOrientationListener.disable();
handler.removeMessages(FIRST_TIME_INIT);
handler.removeMessages(CLEAR_SCREEN_DELAY);
super.onPause();
}
The SurfaceHolder.Callbacks methods have been implemented here:
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
if(holder.getSurface()==null)
{
Log.d(TAG, "holder.getSurface()=null");
return;
}
//stash the holder here for later use,useful if onResume needs to be invoked after this.
surfaceHolder=holder;
if(mCamera==null)
return;
//sometimes surfaceChanged is called after onPause or before onResume,ignore it
if(mPausing || isFinishing())
return;
if(mStatus==PREVIEW_STOPPED)
try {
startPreview();
} catch (CameraHardwareException e1) {
Log.e(TAG, "Error starting the preview");
}
if(!mPreviewing)
try {
startPreview();
} catch (CameraHardwareException e) {
Log.e(TAG,"Could not get the camera");
}
if(!mFirstTimeInitialized)
if(getDisplayRotation(this)!=mDisplayOrientation)
setDisplayOrientation();
if(mPreviewing && holder.isCreating())
{
setPreviewDisplay(holder);
}
if(!mFirstTimeInitialized)
handler.sendEmptyMessage(FIRST_TIME_INIT);
else
initializeSecondTime();
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
//nothing here
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
// TODO Auto-generated method stub
stopPreview();
surfaceHolder=null;
}
The orientation event listener is enabled in onResume and surfaceChanged whichever gets called fastest I guess and is then disabled in onPause.The OrientationEventListener is only instantiated in initializeFirstTime.
Is this happening because I did not enable the OrientationEventListener
correctly.Also did I set the rotation for parameters which affects image rotation correctly:
mParameters.setRotation(rotation);
Upvotes: 3
Views: 3396
Reputation: 31
Call this when surface is created
private void setUpCamera(Camera c) {
Camera.CameraInfo info = new Camera.CameraInfo();
Camera.getCameraInfo(cameraId, info);
rotation = getWindowManager().getDefaultDisplay().getRotation();
int degree = 0;
switch (rotation) {
case Surface.ROTATION_0:
degree = 0;
break;
case Surface.ROTATION_90:
degree = 90;
break;
case Surface.ROTATION_180:
degree = 180;
break;
case Surface.ROTATION_270:
degree = 270;
break;
default:
break;
}
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
// frontFacing
rotation = (info.orientation + degree) % 360;
rotation = (360 - rotation) % 360;
}
else {
// Back-facing
rotation = (info.orientation - degree + 360) % 360;
}
c.setDisplayOrientation(rotation);
params.setRotation(rotation);
camera.setParameters(params);
}
Upvotes: 1