问题
I am developing an android camera app, using autofocus.
The issue, i am able to capture for the first time, but when i am trying to capture for the second time the below exception occurs.
4-19 11:28:32.509: E/AndroidRuntime(4409): FATAL EXCEPTION: main
04-19 11:28:32.509: E/AndroidRuntime(4409): java.lang.RuntimeException: takePicture failed
04-19 11:28:32.509: E/AndroidRuntime(4409): at android.hardware.Camera.native_takePicture(Native Method)
04-19 11:28:32.509: E/AndroidRuntime(4409): at android.hardware.Camera.takePicture(Camera.java:878)
04-19 11:28:32.509: E/AndroidRuntime(4409): at android.hardware.Camera.takePicture(Camera.java:842)
04-19 11:28:32.509: E/AndroidRuntime(4409): at com.example.camerasample.MainActivity$4.onClick(MainActivity.java:60)
04-19 11:28:32.509: E/AndroidRuntime(4409): at android.view.View.performClick(View.java:2485)
04-19 11:28:32.509: E/AndroidRuntime(4409): at android.view.View$PerformClick.run(View.java:9080)
04-19 11:28:32.509: E/AndroidRuntime(4409): at android.os.Handler.handleCallback(Handler.java:587)
04-19 11:28:32.509: E/AndroidRuntime(4409): at android.os.Handler.dispatchMessage(Handler.java:92)
04-19 11:28:32.509: E/AndroidRuntime(4409): at android.os.Looper.loop(Looper.java:130)
04-19 11:28:32.509: E/AndroidRuntime(4409): at android.app.ActivityThread.main(ActivityThread.java:3687)
04-19 11:28:32.509: E/AndroidRuntime(4409): at java.lang.reflect.Method.invokeNative(Native Method)
04-19 11:28:32.509: E/AndroidRuntime(4409): at java.lang.reflect.Method.invoke(Method.java:507)
04-19 11:28:32.509: E/AndroidRuntime(4409): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:867)
04-19 11:28:32.509: E/AndroidRuntime(4409): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:625)
04-19 11:28:32.509: E/AndroidRuntime(4409): at dalvik.system.NativeStart.main(Native Method)
But works fine while capturig in a camera without autofocus functionality.
Below i have attached my codes
MainActivity.java
package com.example.camerasample;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import com.example.camerasample.R;
import android.app.Activity;
import android.content.Context;
import android.hardware.Camera;
import android.hardware.Camera.AutoFocusCallback;
import android.hardware.Camera.PictureCallback;
import android.hardware.Camera.ShutterCallback;
import android.os.Bundle;
import android.util.Log;
import android.view.SurfaceView;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnLongClickListener;
import android.view.ViewGroup.LayoutParams;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.FrameLayout;
public class MainActivity extends Activity {
private static final String TAG = "CamTestActivity";
Preview preview;
Button buttonClick;
Camera camera;
String fileName;
Activity act;
Context ctx;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ctx = this;
act = this;
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.activity_main);
preview = new Preview(this, (SurfaceView)findViewById(R.id.surfaceView));
preview.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
((FrameLayout) findViewById(R.id.preview)).addView(preview);
preview.setKeepScreenOn(true);
buttonClick = (Button) findViewById(R.id.buttonClick);
buttonClick.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
camera.takePicture(shutterCallback, rawCallback, jpegCallback);
}
});
buttonClick.setOnLongClickListener(new OnLongClickListener(){
@Override
public boolean onLongClick(View arg0) {
camera.autoFocus(new AutoFocusCallback(){
@Override
public void onAutoFocus(boolean arg0, Camera arg1) {
}
});
return true;
}
});
}
@Override
protected void onResume() {
super.onResume();
camera = Camera.open();
camera.startPreview();
preview.setCamera(camera);
}
@Override
protected void onPause() {
if(camera != null) {
camera.stopPreview();
preview.setCamera(null);
camera.release();
camera = null;
}
super.onPause();
}
private void resetCam() {
camera.startPreview();
preview.setCamera(camera);
}
ShutterCallback shutterCallback = new ShutterCallback() {
public void onShutter() {
// Log.d(TAG, "onShutter'd");
}
};
PictureCallback rawCallback = new PictureCallback() {
public void onPictureTaken(byte[] data, Camera camera) {
// Log.d(TAG, "onPictureTaken - raw");
}
};
PictureCallback jpegCallback = new PictureCallback() {
public void onPictureTaken(byte[] data, Camera camera) {
FileOutputStream outStream = null;
try {
// Write to SD Card
fileName = String.format("/sdcard/camtest/%d.jpg", System.currentTimeMillis());
outStream = new FileOutputStream(fileName);
outStream.write(data);
outStream.close();
Log.d(TAG, "onPictureTaken - wrote bytes: " + data.length);
resetCam();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
}
Log.d(TAG, "onPictureTaken - jpeg");
}
};
}
Preview.java
package com.example.camerasample;
import java.io.IOException;
import java.util.List;
import android.content.Context;
import android.hardware.Camera;
import android.hardware.Camera.Size;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
class Preview extends ViewGroup implements SurfaceHolder.Callback {
private final String TAG = "Preview";
SurfaceView mSurfaceView;
SurfaceHolder mHolder;
Size mPreviewSize;
List<Size> mSupportedPreviewSizes;
Camera mCamera;
@SuppressWarnings("deprecation")
Preview(Context context, SurfaceView sv) {
super(context);
mSurfaceView = sv;
mHolder = mSurfaceView.getHolder();
mHolder.addCallback(this);
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
public void setCamera(Camera camera) {
mCamera = camera;
if (mCamera != null) {
mSupportedPreviewSizes = mCamera.getParameters().getSupportedPreviewSizes();
requestLayout();
// get Camera parameters
Camera.Parameters params = mCamera.getParameters();
List<String> focusModes = params.getSupportedFocusModes();
if (focusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO)) {
// set the focus mode
params.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
// set Camera parameters
mCamera.setParameters(params);
}
}
}
@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);
}
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
if (changed && getChildCount() > 0) {
final View child = getChildAt(0);
final int width = r - l;
final int height = b - t;
int previewWidth = width;
int previewHeight = height;
if (mPreviewSize != null) {
previewWidth = mPreviewSize.width;
previewHeight = mPreviewSize.height;
}
// Center the child SurfaceView within the parent.
if (width * previewHeight > height * previewWidth) {
final int scaledChildWidth = previewWidth * height / previewHeight;
child.layout((width - scaledChildWidth) / 2, 0,
(width + scaledChildWidth) / 2, height);
} else {
final int scaledChildHeight = previewHeight * width / previewWidth;
child.layout(0, (height - scaledChildHeight) / 2,
width, (height + scaledChildHeight) / 2);
}
}
}
public void surfaceCreated(SurfaceHolder holder) {
// The Surface has been created, acquire the camera and tell it where
// to draw.
try {
if (mCamera != null) {
mCamera.setPreviewDisplay(holder);
}
} catch (IOException exception) {
Log.e(TAG, "IOException caused by setPreviewDisplay()", exception);
}
}
public void surfaceDestroyed(SurfaceHolder holder) {
// Surface will be destroyed when we return, so stop the preview.
if (mCamera != null) {
mCamera.stopPreview();
}
}
private Size getOptimalPreviewSize(List<Size> sizes, int w, int h) {
final double ASPECT_TOLERANCE = 0.1;
double targetRatio = (double) w / h;
if (sizes == null) return null;
Size optimalSize = null;
double minDiff = Double.MAX_VALUE;
int targetHeight = h;
// Try to find an size match aspect ratio and size
for (Size size : sizes) {
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;
}
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
if(mCamera != null) {
Camera.Parameters parameters = mCamera.getParameters();
parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
requestLayout();
mCamera.setParameters(parameters);
mCamera.startPreview();
}
}
}
Manifest:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.camerasample"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk android:minSdkVersion="8" />
<uses-feature android:name="android.hardware.camera" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.hardware.camera.autofocus" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name" >
<activity
android:label="@string/app_name"
android:name=".MainActivity"
android:screenOrientation="landscape" >
<intent-filter >
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
I couldnt find exact solution anywhere, please help.
回答1:
I propose two solutions that worked for me. 1) Stop and Resume the camera correctly. I do it by calling these methods on onPause and onResume, also in the middle of the camera Preview, where I scan QR codes in my app:
public void stopCamera(){
mCamera.cancelAutoFocus();
mCamera.setPreviewCallback(null);
mCamera.stopPreview();
mPreviewing = false;
}
public void rethrottleCamera(){
updateViews(); //Updates my Layouts
mPreviewing = true;
mCamera.startPreview();
mCamera.setPreviewCallback(previewCb);
mCamera.autoFocus(autoFocusCB);
}
2)Very tricky but worked like magic! Make sure that you call autofocus AFTER the preview surface has been created. To do this run Autofocus with a 200ms delay, to buy time for the surface to be created. Set this by pressing ctrl+clic over a "CameraPreview" object declaration, such as:
CameraPreview my_camera;
Look for the "public void surfaceChanged" method and make this changes:
//Add a delay to AUTOFOCUS after mCamera.startpreview();!!:
mCamera.startPreview();
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
mCamera.autoFocus(autoFocusCallback);
}
}, 200); //<-200 millisecond delay
//If you call autofocus right after startPreview, chances are,
//that the previewSurface will have not been created yet,
//and autofocus will fail:
mCamera.startPreview(); //Bad idea!
mCamera.autoFocus(autoFocusCallback); //Bad idea!
There are plenty other fixes, but these two may save your day.
回答2:
Yes, the code crashes when you autofocus on the camera twice. You can workaround this issue, by having a flag, which will check if the autofocus has started, and then skip the execution of the autofocus again.
来源:https://stackoverflow.com/questions/16098474/takepicture-failed-after-autofocus-for-the-second-time