I'm trying to read the focus distance (distance of subject in a photo) from an Android camera. I keep getting 0 for all focus distances on my HTC Desire even when it correctly autofocuses. Here's the whole app, only works on v2.3.3 and above.
ImageCapture.java
package test.test;
import java.io.IOException;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import android.app.Activity;
import android.content.Intent;
import android.graphics.PixelFormat;
import android.hardware.Camera;
import android.hardware.Camera.AutoFocusCallback;
import android.hardware.Camera.PictureCallback;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore.Images.Media;
import android.util.Log;
import android.view.KeyEvent;
import android.view.MenuItem;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
//THIS CLASS READS THE FOCUS DISTANCES
class ImageFocusCallback implements AutoFocusCallback {
@Override
public void onAutoFocus(boolean success, Camera camera) {
//READ FOCUS DISTANCES HERE
Camera.Parameters parameters = camera.getParameters();
float[] distances = new float[3];
if (success) {
// Only available with Android 9 (2.3)
// Focus Mode is always reported as auto but
// distances do not appear to be updating
// always: 0.1, 1.2, Infinity, (on my device it's 0,0,0)
Log.d("Focus Mode: ", parameters.getFocusMode());
parameters.getFocusDistances(distances);
Log.d("focus distance near", Float.toString(distances[0]));
Log.d("focus distance optimum", Float.toString(distances[1]));
Log.d("focus distance far", Float.toString(distances[2]));
}
}
}
public class ImageCapture extends Activity implements SurfaceHolder.Callback {
//CALL AUTO FOCUS HERE
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
ImageFocusCallback autoFocusCallBack = new ImageFocusCallback();
//AUTOFOCUS IS CALLED HERE
camera.autoFocus(autoFocusCallBack);
return true;
}
return false;
}
//REST OF THE CODE
private Camera camera;
private boolean isPreviewRunning = false;
private SimpleDateFormat timeStampFormat = new SimpleDateFormat("yyyyMMddHHmmssSS");
private SurfaceView surfaceView;
private SurfaceHolder surfaceHolder;
private Uri target = Media.EXTERNAL_CONTENT_URI;
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
Log.e(getClass().getSimpleName(), "onCreate");
getWindow().setFormat(PixelFormat.TRANSLUCENT);
setContentView(R.layout.main);
surfaceView = (SurfaceView) findViewById(R.id.surface);
surfaceHolder = surfaceView.getHolder();
surfaceHolder.addCallback(this);
surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
@Override
public boolean onCreateOptionsMenu(android.view.Menu menu) {
MenuItem item = menu.add(0, 0, 0, "goto gallery");
item.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
Intent intent = new Intent(Intent.ACTION_VIEW, target);
startActivity(intent);
return true;
}
});
return true;
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
}
Camera.PictureCallback mPictureCallbackRaw = new Camera.PictureCallback() {
@Override
public void onPictureTaken(byte[] data, Camera c) {
Log.e(getClass().getSimpleName(), "PICTURE CALLBACK RAW: " + data);
camera.startPreview();
}
};
Camera.PictureCallback mPictureCallbackJpeg = new Camera.PictureCallback() {
@Override
public void onPictureTaken(byte[] data, Camera c) {
Log.e(getClass().getSimpleName(), "PICTURE CALLBACK JPEG: data.length = " + data);
}
};
Camera.ShutterCallback mShutterCallback = new Camera.ShutterCallback() {
@Override
public void onShutter() {
Log.e(getClass().getSimpleName(), "SHUTTER CALLBACK");
}
};
// ImageCaptureCallback iccb = null;
// if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
// try {
// String filename = timeStampFormat.format(new Date());
// ContentValues values = new ContentValues();
// values.put(Media.TITLE, filename);
// values.put(Media.DESCRIPTION, "Image capture by camera");
// Uri uri = getContentResolver().insert(Media.EXTERNAL_CONTENT_URI,
// values);
// // String filename = timeStampFormat.format(new Date());
// iccb = new
// ImageCaptureCallback(getContentResolver().openOutputStream(uri));
// } catch (Exception ex) {
// ex.printStackTrace();
// Log.e(getClass().getSimpleName(), ex.getMessage(), ex);
// }
// }
// if (keyCode == KeyEvent.KEYCODE_BACK) {
// return super.onKeyDown(keyCode, event);
// }
//
// if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
// camera.takePicture(mShutterCallback, mPictureCallbackRaw, iccb);
// return true;
// }
//
// return false;
// }
@Override
protected void onResume() {
Log.e(getClass().getSimpleName(), "onResume");
super.onResume();
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
}
@Override
protected void onStop() {
Log.e(getClass().getSimpleName(), "onStop");
super.onStop();
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
Log.e(getClass().getSimpleName(), "surfaceCreated");
camera = Camera.open();
Camera.Parameters parameters = camera.getParameters();
float[] distances = new float[3];
Log.d("Focus Mode: ", parameters.getFocusMode());
parameters.getFocusDistances(distances);
Log.d("focus distance 0", Float.toString(distances[0]));
Log.d("focus distance 1", Float.toString(distances[1]));
Log.d("focus distance 2", Float.toString(distances[2]));
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
Log.e(getClass().getSimpleName(), "surfaceChanged");
if (isPreviewRunning) {
camera.stopPreview();
}
Camera.Parameters p = camera.getParameters();
p.setPreviewSize(w, h);
camera.setParameters(p);
try {
camera.setPreviewDisplay(holder);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
camera.startPreview();
isPreviewRunning = true;
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
Log.e(getClass().getSimpleName(), "surfaceDestroyed");
camera.stopPreview();
isPreviewRunning = false;
camera.release();
}
}
class ImageCaptureCallback implements PictureCallback {
private OutputStream filoutputStream;
public ImageCaptureCallback(OutputStream filoutputStream) {
this.filoutputStream = filoutputStream;
}
@Override
public void onPictureTaken(byte[] data, Camera camera) {
try {
Log.v(getClass().getSimpleName(), "onPictureTaken=" + data + " length = " + data.length);
filoutputStream.write(data);
filoutputStream.flush();
filoutputStream.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
}
main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<SurfaceView android:id="@+id/surface"
android:layout_width="fill_parent"
android:layout_height="10dip"
android:layout_weight="1">
</SurfaceView>
</LinearLayout>
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="test.test"
android:versionCode="1"
android:versionName="1.0">
<uses-sdk android:minSdkVersion="10" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />
<application android:icon="@drawable/icon" android:label="@string/app_name">
<activity android:name=".ImageCapture"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Could it be that that there is a bug in the device driver? In the Android source Camera.java call native_autoFocus() and native_getParameters() to read the distances. Does anyone know where to get the source for the native calls?
In Froyo, frameworks/base/core/jni/android_hardware_Camera.cpp implements native_autoFocus() by calling android_hardware_Camera_autoFocus(), which appears to bind at runtime to the device/vendor specific code.
Unfortunately, "If the camera does not support auto-focus and autoFocus is called, onAutoFocus will be called immediately with a fake value of success set to true." is a documented behavior.
I'm curious which of the functions you use requires 2.3.3, API level 10.
This is Bug 14341 - "Distances returned by Camera.Parameters.getFocusDistances() do not change on Nexus S regardless of actual focus distance".
First reported Jan 26, 2011 with the last report on Mar 18, 2014. Thus, the Bug was reported shortly before you ran into it and has not been fixed to this day. It is specifically reported against your Device.
Various comments in the report claim this Bug affects the "Nexus S", "HTC Sensation", "Galaxy S Plus", "Galaxy S II", "HTC Desire HD", "Galaxy Nexus", "Xperia Mini ST15i", "Nexus 5", "Galaxy R", and the "Galaxy Note II"; with one Comment claiming it works perfectly on the "Galaxy Nexus".
Reference: https://code.google.com/p/android/issues/detail?id=14341 .
Is this the Source Code you were looking for: https://android.googlesource.com/platform/frameworks/base/+/froyo-release/core/jni/android_hardware_Camera.cpp
I don't know where you can get the source for the native calls. But what about getting 0 for all the focus distances in getFocusDistances(float[]) method, you can read this issue. There you can read that this error is not the error of the device but of the Android SDK.
Probably its a bug in the camera driver you can only fix this if you set up you phone again, yes I know it really shitty, but I had exact the same problem too.
But i choosed to use a custom rom for my device. Atm im using Android Revolution HD 6.1.1 http://forum.xda-developers.com/showthread.php?t=840040
For your secound Question you can take out the source file for the camera app from the package you download with the rom. Probably if you change it, it will work.
Best Regards