I have implemented a custom camera in my android app but when I rotate my device my surface view is rotated but the issue is it shrinks and I want same ratio image from both the orientation.
In my portrait view, my Preview is shrinking but I got the full-size image for example when I take the image from landscape view I am getting the image of size 1080*960 where 1080 is width and 960 is height as well when I clicked the image from portrait view height of image is 1080 and width is 960 but I want image as same as landscape mode
Below is my Surface code
public void surfaceCreated(SurfaceHolder holder) {
mSurfaceHolder = holder;
if (mCamera != null) {
stopCameraPreview();
mCamera.release();
}
getCamera(mCameraID);
setCameraDisplayOrientation(activity,mCameraID,mCamera);
startCameraPreview();
}
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);
}
private Camera.Size getOptimalPreviewSize(List<Camera.Size> sizes, int w, int h) {
final double ASPECT_TOLERANCE = 0.1;
double targetRatio = (double) h / w;
if (sizes == null)
return null;
Camera.Size optimalSize = null;
double minDiff = Double.MAX_VALUE;
int targetHeight = h;
for (Camera.Size size : sizes) {
double ratio = (double) size.height / size.width;
if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE)
continue;
if (Math.abs(size.height - targetHeight) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}
if (optimalSize == null) {
minDiff = Double.MAX_VALUE;
for (Camera.Size size : sizes) {
if (Math.abs(size.height - targetHeight) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}
}
return optimalSize;
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
try {
mCamera.startPreview();
mIsSafeToTakePhoto = true;
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
// The surface is destroyed with the visibility of the SurfaceView is set to View.Invisible
try {
if (mCamera != null) {
stopCameraPreview();
mCamera.release();
}
} catch (Exception e) {
e.printStackTrace();
}
}
Related
I have implemented my custom CamerView with TextureView which works perfectly fine. Though in some devices such as Motorolla G4 - MarshMallow, Lenovo KNote - Lollipop Camera Preview flickers with Green Square View appears on the screen.
Here's my CameraTextureView
public class CameraTextureView extends TextureView implements TextureView.SurfaceTextureListener {
private static final String TAG = CameraTextureView.class.getSimpleName();
private Camera mCamera;
private List<Camera.Size> mSupportedPreviewSizes;
private Camera.Size mPreviewSize;
private SurfaceTexture mSurfaceTexture;
private Handler mHandler;
private Context mContext;
public void setCamera(Camera mCamera) {
try {
this.mCamera = mCamera;
if (mCamera == null) {
return;
}
startCameraPreview();
} catch (Exception e) {
FileLog.e(TAG, e);
}
}
public Camera getCamera() {
return mCamera;
}
public CameraTextureView(Context context, Camera mCamera) {
super(context);
try {
this.setSurfaceTextureListener(this);
this.mCamera = mCamera;
this.mSurfaceTexture = getSurfaceTexture();
this.mHandler = new Handler();
mSupportedPreviewSizes = mCamera.getParameters().getSupportedPreviewSizes();
this.mContext = context;
} catch (Exception e) {
FileLog.e(TAG, e);
}
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
try {
final int width = resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec);
final int height = resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec);
if (mSupportedPreviewSizes != null) {
mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, width, height);
}
float ratio;
if (mPreviewSize.height >= mPreviewSize.width)
ratio = (float) mPreviewSize.height / (float) mPreviewSize.width;
else
ratio = (float) mPreviewSize.width / (float) mPreviewSize.height;
setMeasuredDimension(width, ApplicationLoader.getPreferences().isToShowTeamList() == true ? (int) (width * ratio) : height);
} catch (Exception e) {
FileLog.e(TAG, e);
}
}
private Camera.Size getOptimalPreviewSize(List<Camera.Size> sizes, int w, int h) {
try {
final double ASPECT_TOLERANCE = 0.1;
double targetRatio = (double) h / w;
if (sizes == null) return null;
Camera.Size optimalSize = null;
double minDiff = Double.MAX_VALUE;
int targetHeight = h;
for (Camera.Size size : sizes) {
double ratio = (double) size.width / size.height;
if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue;
if (Math.abs(size.height - targetHeight) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}
if (optimalSize == null) {
minDiff = Double.MAX_VALUE;
for (Camera.Size size : sizes) {
if (Math.abs(size.height - targetHeight) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}
}
return optimalSize;
} catch (Exception e) {
FileLog.e(TAG, e);
}
return null;
}
public void startCameraPreview() {
try {
Runnable runnable = new Runnable() {
#Override
public void run() {
try {
mCamera.setPreviewTexture(getSurfaceTexture());
Camera.Parameters parameters = mCamera.getParameters();
parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
parameters.setRotation(90);
mCamera.setDisplayOrientation(90);
mCamera.setParameters(parameters);
mCamera.startPreview();
} catch (Exception e) {
FileLog.e(TAG, e);
}
}
};
Thread thread = new Thread(runnable);
thread.start();
} catch (Exception e) {
FileLog.e(TAG, e);
}
}
private void setCameraDisplayOrientation(Context mContext, android.hardware.Camera camera) {
try{
android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo();
/*int rotation = mContext.getWindowManager().getDefaultDisplay().getRotation();*/
Display display = ((WindowManager) mContext.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 (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){
FileLog.e(TAG, e.toString());
}
}
#Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
startCameraPreview();
}
#Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
}
#Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
return false;
}
#Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
}
}
Though while taking screenshot it appears normal on screenshot.
I have built a android camera app, which works fine for few devices and in some devices it crashes before starting the preview. Tried with all stackOverflow solution, but nothing worked for me.
My Preview Class
public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
protected List<Camera.Size> mSupportedPreviewSizes;
private List<Camera.Size> mSupportedPictureSizes;
private Camera.Size mPreviewSize;
private Camera.Size mPictureSize;
private Camera mCamera;
public CameraPreview(Context context) {
super(context);
}
public CameraPreview(Activity activity, Camera camera) {
super(activity);
mCamera = camera;
setCamera(mCamera);
mCamera.getParameters().getSupportedPreviewFpsRange();
SurfaceHolder mHolder = getHolder();
mHolder.addCallback(this);
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
mHolder.setKeepScreenOn(true);
}
public void setCamera(Camera camera) {
mCamera = camera;
if (mCamera != null) {
mCamera.setDisplayOrientation(getDisplayOrientation());
Camera.Parameters parameters = mCamera.getParameters();
mSupportedPreviewSizes = parameters.getSupportedPreviewSizes();
mSupportedPictureSizes = parameters.getSupportedPictureSizes();
requestLayout();
parameters.setWhiteBalance(Camera.Parameters.WHITE_BALANCE_AUTO);
parameters.setSceneMode(Camera.Parameters.SCENE_MODE_AUTO);
int index = parameters.getExposureCompensation();
parameters.setExposureCompensation(index);
mCamera.setParameters(parameters);
}
}
public int getDisplayOrientation() {
Camera.CameraInfo info = new Camera.CameraInfo();
Camera.getCameraInfo(Camera.CameraInfo.CAMERA_FACING_FRONT, info);
WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
int rotation = display.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;
}
int result;
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
result = (info.orientation + degree) % 360;
result = (360 - result) % 360;
} else {
result = (info.orientation - degree + 360) % 360;
}
return result;
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
try {
if (mCamera != null) {
mCamera.setPreviewDisplay(holder);
}
} catch (IOException e) {
Log.d("DG_DEBUG", "Error setting camera preview in surfaceCreated: " + e.getMessage());
}
}
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int width = resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec);
final int height = resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec);
setMeasuredDimension(width, height);
if (mSupportedPreviewSizes != null) {
mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, width, height);
}
if(mSupportedPictureSizes != null){
mPictureSize = getOptimalPictureSize();
}
}
private Camera.Size getOptimalPreviewSize(List<Camera.Size> sizes, int width, int height) {
Camera.Size optimalSize = null;
final double ASPECT_TOLERANCE = 0.01;
double targetRatio = (double) height / width;
double minDiff = Double.MAX_VALUE;
int targetHeight = height;
for (Camera.Size size : sizes) {
double ratio = (double) size.width / size.height;
if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue;
if (Math.abs(size.height - targetHeight) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}
if (optimalSize == null) {
minDiff = Double.MAX_VALUE;
for (Camera.Size size : sizes) {
if (Math.abs(size.height - targetHeight) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}
}
// Here it is returning optimal preview size.
For Example : Moto G has 720*1280 screen resolution, it works perfectly fine for by returning optimalSize as 720*1280.
But for Moto X which has same resolution 720*1280 it crashes before starting the preview. It is return width = 640, height=480 which is incorrect.
Log.d("getOptimalPreviewSize",
String.format(
"getOptimalPreviewSize result: width = %d, height = %d for input width = %d, height = %d",
optimalSize.width, optimalSize.height, width,
height));
return optimalSize;
}
private Camera.Size getOptimalPictureSize() {
if (mCamera == null) {
return null;
}
List<Camera.Size> cameraSizes = mCamera.getParameters().getSupportedPictureSizes();
Camera.Size optimalSize = mCamera.new Size(0, 0);
double previewRatio = (double) mPreviewSize.width / mPreviewSize.height;
for (Camera.Size size : cameraSizes) {
double ratio = (double) size.width / size.height;
if (Math.abs(ratio - previewRatio) > 0.01f)
continue;
if (size.height > optimalSize.height) {
optimalSize = size;
}
}
if (optimalSize.height == 0) {
for (Camera.Size size : cameraSizes) {
if (size.height > optimalSize.height) {
optimalSize = size;
}
}
}
return optimalSize;
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
if (mCamera != null) {
Camera.Parameters parameters = mCamera.getParameters();
List<Camera.Size> mSupportedPreviewSizes = parameters.getSupportedPreviewSizes();
Camera.Size mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, width, height);
parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
parameters.setPictureSize(mPictureSize.width, mPictureSize.height);
mCamera.setParameters(parameters);
mCamera.startPreview();
}
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
try {
if (mCamera != null) {
mCamera.stopPreview();
mCamera.release();
}
} catch (Exception e) {
Log.d("SurfaceDestroyed", " Error handling surface destroy method");
}
}
}
Android Version 4.4.2 Samsung Note 8:
I have made a custom camera using surface view.
I may able to capture image and store it in SDcard.
It looks perfect in all devices I checked i.e Nexus 7, Micromax Turbo, Canvas Hd, Moto G, Moto E.
But In samsung Note 8(4.4.2 - Kitkat), Image got tile 1/8 times. some times 2/8.
When user captured a photo and stored it in SdCard folder, It contains tiled image.
Like this: http://prntscr.com/5xoc5e
Camera Capturing Code:
OnCapture Button Click:
camera.takePicture(null, null, mPicture);
PictureCallback mPicture = new PictureCallback()
{
#Override
public void onPictureTaken(byte[] data, Camera camera)
{
mrawDataCapturedImg = data;
try
{
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 2;
Bitmap bmp = BitmapFactory.decodeByteArray(mrawDataCapturedImg, 0, mrawDataCapturedImg.length,options);
mimgPreview.setImageBitmap(bmp);
}
catch (Exception e)
{
e.printStackTrace();
}
}
};
Storing Image Code:
File pictureFile = new File(mediaStorageDir.getPath()
+ File.separator + saveName + ".jpg");
try
{
FileOutputStream fos = new FileOutputStream(pictureFile);
fos.write(mrawDataCapturedImg);
fos.close();
}
catch (FileNotFoundException e)
{
e.printStackTrace();
}
catch (IOException e)
{
e.printStackTrace();
}
MyCameraPreview Class:
public class MyCameraPreview extends SurfaceView implements SurfaceHolder.Callback
{
private SurfaceHolder mSurfaceHolder;
private Camera mCamera;
PreviewCallback mPreviewCallback;
AutoFocusCallback mAutoFocusCallback;
#SuppressWarnings("unused")
private String TAG = "camera";
private Activity context;
Size mPreviewSize;
List<Size> mSupportedPreviewSizes;
#SuppressWarnings("deprecation")
public MyCameraPreview(Activity context, Camera camera, AutoFocusCallback autoFocusCB,PreviewCallback previewCallback)
{
super(context);
mPreviewCallback = previewCallback;
this.context = context;
this.mCamera = camera;
mAutoFocusCallback = autoFocusCB;
this.mSurfaceHolder = MyCameraPreview.this.getHolder();
this.mSurfaceHolder.addCallback(MyCameraPreview.this);
this.mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
#Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int format, int width, int height)
{
try
{
if(mCamera!=null)
{
// Now that the size is known, set up the camera parameters and begin
// the preview.
Camera.Parameters parameters = mCamera.getParameters();
parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
mCamera.setParameters(parameters);
setCameraDisplayOrientation(0, mCamera);
mCamera.setPreviewDisplay(surfaceHolder);
mCamera.setPreviewCallback(mPreviewCallback);
mCamera.startPreview();
mCamera.autoFocus(mAutoFocusCallback);
}
}
catch (Exception e)
{
e.printStackTrace();
}
}
#Override
public void surfaceCreated(SurfaceHolder surfaceHolder)
{
try {
if (mCamera != null)
{
mCamera.setPreviewDisplay(surfaceHolder);
}
} catch (IOException exception) {
Log.e(TAG, "IOException caused by setPreviewDisplay()", exception);
}
}
#Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder)
{
if (mCamera != null)
{
if(mAutoFocusCallback != null)
mCamera.cancelAutoFocus();
mCamera.stopPreview();
}
}
public void setCameraDisplayOrientation(
int cameraId, android.hardware.Camera camera)
{
android.hardware.Camera.CameraInfo info =
new android.hardware.Camera.CameraInfo();
android.hardware.Camera.getCameraInfo(cameraId, info);
int rotation = context.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);
}
public void setCamera(Camera camera)
{
mCamera = camera;
if (mCamera != null)
{
mSupportedPreviewSizes = mCamera.getParameters().getSupportedPreviewSizes();
}
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
// We purposely disregard child measurements because act as a
// wrapper to a SurfaceView that centers the camera preview instead
// of stretching it.
final int width = resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec);
final int height = resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec);
setMeasuredDimension(width, height);
if (mSupportedPreviewSizes != null)
{
mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, width, height);
}
}
private Size getOptimalPreviewSize(List<Size> sizes, int w, int h)
{
final double ASPECT_TOLERANCE = 0.1;
double targetRatio = (double) w / h;
if (sizes == null) return null;
Size optimalSize = null;
double minDiff = Double.MAX_VALUE;
int targetHeight = h;
// Try to find an size match aspect ratio and size
for (Size size : sizes) {
double ratio = (double) size.width / size.height;
if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue;
if (Math.abs(size.height - targetHeight) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}
// Cannot find the one match the aspect ratio, ignore the requirement
if (optimalSize == null)
{
minDiff = Double.MAX_VALUE;
for (Size size : sizes)
{
if (Math.abs(size.height - targetHeight) < minDiff)
{
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}
}
return optimalSize;
}
}
I'm having issues with the aspect ratio of a MediaRecorder in my Android app. I'm having the issue specifically with the Samsung Galaxy S II, whose video camera seems to zoom in compared to the regular camera (this is a behavior I notice when using the default camera app on the phone as well).
You can see how the aspect ratio is stretched when I switch from using the camera to using the MediaRecorder in this video:
https://www.youtube.com/watch?v=U8vCwiNjCPU
and in the screenshots below:
Camera aspect ratio (correct):
Video aspect ratio (incorrect):
How can I ensure that the aspect ratio of the video preview is correct?
Here is my code:
CustomCamera activity:
public class CustomCamera extends SherlockActivity {
private boolean prepareVideoRecorder() {
Log.d(TAG, "in prepareVideoRecorder()");
// It is very important to unlock the camera before doing setCamera
// or it will results in a black preview
if (camera == null)
{
camera = getCameraInstance();
}
if (recorder == null){
recorder = new MediaRecorder();
}
//Have to stop preview before starting to record
camera.stopPreview();
// Step 1: Unlock and set camera to MediaRecorder
camera.unlock();
recorder.setCamera(camera);
// Step 2: Set sources
recorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
recorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
// Step 3: Set a CamcorderProfile (requires API Level 8 or higher)
recorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH));
// Step 4: Set output file
recorder.setOutputFile(getOutputMediaFile(MEDIA_TYPE_VIDEO).getAbsolutePath());
// No limit. Don't forget to check the space on disk.
recorder.setMaxDuration(50000);
recorder.setVideoFrameRate(30);
recorder.setVideoEncodingBitRate(3000000);
recorder.setAudioEncodingBitRate(8000);
// Step 5: Set the preview output
recorder.setPreviewDisplay(cameraPreview.getHolder().getSurface());
//Setting the camera's orientation
int degree = 0;
// do not rotate image, just put rotation info in
switch (mOrientation) {
case ORIENTATION_LANDSCAPE_INVERTED:
degree = 180;
break;
case ORIENTATION_PORTRAIT_NORMAL:
degree = 90;
break;
case ORIENTATION_LANDSCAPE_NORMAL:
degree = 0;
break;
case ORIENTATION_PORTRAIT_INVERTED:
degree = 270;
break;
}
recorder.setOrientationHint(degree);
// Step 6: Prepare configured MediaRecorder
try {
recorder.prepare();
} catch (IllegalStateException e) {
// This is thrown if the previous calls are not called with the
// proper order
e.printStackTrace();
releaseMediaRecorder();
return false;
} catch (IOException e) {
releaseMediaRecorder();
e.printStackTrace();
return false;
}
//Everything went successfully
return true;
}
}
/**
* Method used to set the camera preview's parameters to match the
* phone's width and set the height accordingly to assure that there are
* no aspect ratio issues.
*/
private void setHolderParameters() {
Log.d(TAG, "setting camera layout parameters");
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
int height = metrics.heightPixels;
int width = metrics.widthPixels;
Size mPreviewSize = CameraPreview.getOptimalPreviewSize(camera.getParameters().getSupportedPreviewSizes(), width, height);
double ratio = ((double)mPreviewSize.width)/mPreviewSize.height;
FrameLayout.LayoutParams previewParams = new FrameLayout.LayoutParams(width, (int)(width*ratio));
cameraPreview.setLayoutParams(previewParams);
}
/**
* Open the camera asynchronously to reduce the lag when opening
* activity
*/
public void openCameraAsync(){
new AsyncTask<Object, Object, Object>(){
#Override
protected Object doInBackground(Object... arg0) {
if (!isFinishing()){
//Resuming camera and display when resuming
if(camera == null){
Log.d(TAG, "Resuming with a null camera");
camera = getCameraInstance();
}
}
return null;
}
#Override
protected void onPostExecute(Object result){
setHolderParameters();
cameraPreview.setCamera(camera);
//Calling surface created so that the preview of the camera is correct
cameraPreview.surfaceCreated(cameraPreview.getHolder());
}
}.execute();
}
CameraPreview.java:
public static Size getOptimalPreviewSize(List<Size> sizes, int w, int h) {
Log.d(TAG, "getOptimalPreviewSize");
final double ASPECT_TOLERANCE = 0.05;
double targetRatio = (double) w/h;
if (sizes==null) return null;
Size optimalSize = null;
double minDiff = Double.MAX_VALUE;
int targetHeight = h;
// Find size
for (Size size : sizes) {
double ratio = (double) size.width / size.height;
if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue;
if (Math.abs(size.height - targetHeight) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}
if (optimalSize == null) {
minDiff = Double.MAX_VALUE;
for (Size size : sizes) {
if (Math.abs(size.height - targetHeight) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}
}
Log.d(TAG, "targetRatio: " + targetRatio);
Log.d(TAG, "optimalSize: " + optimalSize);
return optimalSize;
}
public void setRecorder(MediaRecorder recorder){
this.recorder = recorder;
}
Are setting any Camera.Parameters? You need to use setPreviewSize(int width, int height) and set it to the width and height of your video.
In your MediaRecorder, you may also need to use setVideoSize(int,int) and (again) set to the size of your video.
I was having the same issue you are having and to get the correct aspect ratio for your video, the layout size, Camera Preview size, and MediaRecorder size should have the same aspect ratio. Errors usually occur when one of these is off.
On some phones on measure returns different width and height than DisplayMatrics provide and that is why you get shrink-ed or widened picture when you press record.
specifically these values :
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
measuredWidth = resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec);
measuredHeight = resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec);
setMeasuredDimension(measuredWidth, measuredHeight);
//setMeasuredDimension(mPreviewSize.height, mPreviewSize.width);
}
}
from your surface view may be different than:
public static Camera.Size getDeviceSpecificOptimalPreviewSize(Context context, Camera camera, int w, int h) {
List<Camera.Size> mSupportedPreviewSizes = camera.getParameters().getSupportedPreviewSizes();
if (mSupportedPreviewSizes != null) {
final double ASPECT_TOLERANCE = 0.1;
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
int width = metrics.widthPixels;
int height = metrics.heightPixels;
double targetRatio = (double) height / width;
Camera.Size optimalSize = null;
double minDiff = Double.MAX_VALUE;
for (Camera.Size size : mSupportedPreviewSizes) {
double ratio = (double) size.width / size.height;
if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue;
if (Math.abs(size.height - h) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.height - h);
}
}
if (optimalSize == null) {
minDiff = Double.MAX_VALUE;
for (Camera.Size size : mSupportedPreviewSizes) {
if (Math.abs(size.height - h) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.height - h);
}
}
}
return optimalSize;
}
these values that return Camera.Size object. mPreviewSize in my case. You also need to set them on camera object in onSurfaceCreated and in onSurfaceChanged method in your SurfaceView class.
Be careful when setting the values because onMeasure on some phones returns height and width in reverse order.
I 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);
}