Android 攝像頭自動對焦的幾點注意
阿新 • • 發佈:2019-01-10
今天在做手機攝像頭自動對焦時出了一些問題,這裡做個筆記記錄一下。
注意事項:1、初始化Camera的程式碼中要加入下面兩行程式碼
mCamera.autoFocus(myAutoFocusCallback); mCamera.cancelAutoFocus();
示例:
private void initCamera(SurfaceHolder holder) { Log.i(TAG, "initCamera.."); if (mPreviewRunning) { mCamera.stopPreview(); } Camera.Parameters parameters; try { parameters = mCamera.getParameters(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); return; } parameters.setPreviewSize(videoWidth, videoHight); parameters.setPictureFormat(PixelFormat.JPEG); parameters.setPreviewFormat(PixelFormat.YCbCr_420_SP); SetCameraFPS(parameters); setCameraDisplayOrientation(this, curCameraIndex, mCamera); mCamera.setParameters(parameters); int bufferSize = (((videoWidth | 0xf) + 1) * videoHight * ImageFormat.getBitsPerPixel(parameters.getPreviewFormat())) / 8; mCamera.addCallbackBuffer(new byte[bufferSize]); mCamera.setPreviewCallbackWithBuffer(this); try { mCamera.setPreviewDisplay(holder); } catch (Exception ex) { // TODO Auto-generated catch block if (null != mCamera) { mCamera.release(); mCamera = null; } ex.printStackTrace(); } mCamera.startPreview(); mCamera.autoFocus(myAutoFocusCallback); mCamera.cancelAutoFocus(); mPreviewRunning = true; }
myAutoFocusCallback相關程式碼:
private Camera.AutoFocusCallback myAutoFocusCallback = null; ... //自動聚焦變量回調 myAutoFocusCallback = new Camera.AutoFocusCallback() { public void onAutoFocus(boolean success, Camera camera) { if (success)//success表示對焦成功 { Log.i(TAG, "onAutoFocus succeed..."); camera.cancelAutoFocus();//只有加上了這一句,才會自動對焦。 initCamera(mSurfaceHolder); doAutoFocus(); } else { Log.i(TAG, "onAutoFocus failed..."); } } };
注意事項2:也是上面程式碼中的一個聚焦的方法doAutoFocus
//設定聚焦 private void doAutoFocus() { parameters = mCamera.getParameters(); parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO); mCamera.setParameters(parameters); mCamera.autoFocus(new Camera.AutoFocusCallback() { @Override public void onAutoFocus(boolean success, Camera camera) { if (success) { camera.cancelAutoFocus();// 只有加上了這一句,才會自動對焦。 if (!Build.MODEL.equals("KORIDY H30")) { parameters = camera.getParameters(); parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);// 連續自動對焦 camera.setParameters(parameters); } else { parameters = camera.getParameters(); parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO); camera.setParameters(parameters); } } } }); }
這裡面有個註釋是 “連續自動對焦”,如果不選擇這個模式 只能實現一開始自動對焦一次 然後在遠近變化就不對焦了
最後完整程式碼貼一份,後面在找這個程式碼就方便了 不用開啟工程去找:
package com.star.surveillance.live;
import android.Manifest;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.graphics.ImageFormat;
import android.graphics.PixelFormat;
import android.hardware.Camera;
import android.media.AudioManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.support.annotation.RequiresApi;
import android.support.v4.app.ActivityCompat;
import android.util.Log;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.WindowManager;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import com.star.star_common.util.ActivityStyleUtil;
import com.star.star_common.util.ToastUtil;
import com.star.star_common.view.StarTextView;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import static com.star.star_common.constants.StarConstants.TAG;
public class RecordActivity extends Activity implements SurfaceHolder.Callback, Camera.PreviewCallback {
private LinearLayout rightBtnsLL;
private FrameLayout playFL;
private StarTextView backTV;
private ImageView backIV;
private ImageView switchCameraIV;
private ImageView voiceIV;
private StarTextView topBackTV;
private RelativeLayout topTitleRL;
private RelativeLayout backRL;
private int previousVolume = 10;
private View main;
private MyVolumeReceiver mVolumeReceiver;
private Uri fileUri;
private Camera mCamera = null;
private Camera.AutoFocusCallback myAutoFocusCallback = null;
private SurfaceView mSurfaceView = null;
private SurfaceHolder mSurfaceHolder = null;
private static final int FRONT = 1; //前置攝像頭標記
private static final int BACK = 2; //後置攝像頭標記
private int currentCameraType = BACK; //當前開啟的攝像頭標記
private int curCameraIndex = -1;
private boolean mPreviewRunning = false;
private final int videoWidth = 1280;
private final int videoHight = 720;
private int frameCount = 0;
private Camera.Parameters parameters;//相機引數
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityStyleUtil.setStatusTransparentStyle(this, false);
main = getLayoutInflater().inflate(R.layout.activity_record, null);
main.setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_IMMERSIVE);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); //螢幕常亮
setContentView(main);
/*
Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
try {
fileUri = Uri.fromFile(createMediaFile()); // create a file to save the video
} catch (Exception e) {
e.printStackTrace();
}
intent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri); // set the image file name
intent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 1); // set the video image quality to high
// start the Video Capture Intent
startActivityForResult(intent, 1);
*/
int permission = ActivityCompat.checkSelfPermission(this,
android.Manifest.permission.READ_PHONE_STATE);
if (permission != PackageManager.PERMISSION_GRANTED) {
// We don't have permission so prompt the user
ActivityCompat.requestPermissions(this, new String[]{
android.Manifest.permission.RECORD_AUDIO,
android.Manifest.permission.READ_EXTERNAL_STORAGE,
android.Manifest.permission.READ_PHONE_STATE,
android.Manifest.permission.WRITE_EXTERNAL_STORAGE,
android.Manifest.permission.CAMERA,
android.Manifest.permission.CHANGE_WIFI_MULTICAST_STATE,
android.Manifest.permission.INTERNET,
android.Manifest.permission.ACCESS_NETWORK_STATE,
android.Manifest.permission.ACCESS_WIFI_STATE,
android.Manifest.permission.WAKE_LOCK,
android.Manifest.permission.VIBRATE,
android.Manifest.permission.SYSTEM_ALERT_WINDOW,
}, 1);
}
initView();
registerVolumeReceiver();
}
@Override
protected void onDestroy() {
super.onDestroy();
try {
if (mVolumeReceiver != null) {
unregisterReceiver(mVolumeReceiver);
mVolumeReceiver = null;
}
} catch (Exception e) {
e.printStackTrace();
}
}
private void initView() {
backIV = (ImageView) findViewById(R.id.surveillance_common_back_iv);
topTitleRL = (RelativeLayout) findViewById(R.id.surveillance_common_top_rl);
topTitleRL.setBackgroundResource(R.drawable.surveillance_record_back_bj);
topBackTV = (StarTextView) findViewById(R.id.surveillance_common_back_tv);
topBackTV.setText("手機錄製");
playFL = (FrameLayout) findViewById(R.id.surveillance_record_fl);
backTV = (StarTextView) findViewById(R.id.surveillance_common_back_tv);
backRL = (RelativeLayout) findViewById(R.id.surveillance_common_back_rl);
backRL.setOnClickListener(mClickListener);
voiceIV = (ImageView) findViewById(R.id.surveillance_record_mute_iv);
voiceIV.setOnClickListener(mClickListener);
rightBtnsLL = (LinearLayout) findViewById(R.id.surveillance_record_right_btn_ll);
if (getCurrentVolume() == 0) {
voiceIV.setBackgroundResource(R.drawable.surveillance_record_mute);
}
mSurfaceView = (SurfaceView) findViewById(R.id.surface);
mSurfaceView.setOnClickListener(mClickListener);
mSurfaceHolder = mSurfaceView.getHolder();
mSurfaceHolder.addCallback(this);
mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
switchCameraIV = (ImageView) findViewById(R.id.surveillance_record_switch_camera_iv);
switchCameraIV.setOnClickListener(mClickListener);
//自動聚焦變量回調
myAutoFocusCallback = new Camera.AutoFocusCallback() {
public void onAutoFocus(boolean success, Camera camera) {
if (success)//success表示對焦成功
{
Log.i(TAG, "onAutoFocus succeed...");
camera.cancelAutoFocus();//只有加上了這一句,才會自動對焦。
initCamera(mSurfaceHolder);
doAutoFocus();
} else {
Log.i(TAG, "onAutoFocus failed...");
}
}
};
}
private View.OnClickListener mClickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
if (v.getId() == R.id.surveillance_record_mute_iv) {
if (getCurrentVolume() == 0) {
voiceIV.setBackgroundResource(R.drawable.surveillance_record_voice);
} else {
voiceIV.setBackgroundResource(R.drawable.surveillance_record_mute);
}
setStreamVolume();
} else if (v.getId() == R.id.surveillance_common_back_rl) {
onBackPressed();
} else if (v.getId() == R.id.surveillance_record_switch_camera_iv) {
} else if (v.getId() == R.id.surface) {
doAutoFocus();
}
}
};
public void setStreamVolume() {
AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
int volume = am.getStreamVolume(AudioManager.STREAM_MUSIC);
if (volume == 0) {
am.setStreamVolume(AudioManager.STREAM_MUSIC, previousVolume, AudioManager.FLAG_PLAY_SOUND);
} else {
previousVolume = volume;
am.setStreamVolume(AudioManager.STREAM_MUSIC, 0, AudioManager.FLAG_PLAY_SOUND);
}
}
public int getCurrentVolume() {
AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
int volume = am.getStreamVolume(AudioManager.STREAM_MUSIC);
return volume;
}
//動態獲取記憶體儲存許可權
public static void verifyStoragePermissions(Activity activity) {
// Check if we have write permission
int permission = ActivityCompat.checkSelfPermission(activity,
Manifest.permission.WRITE_EXTERNAL_STORAGE);
if (permission != PackageManager.PERMISSION_GRANTED) {
// We don't have permission so prompt the user
ActivityCompat.requestPermissions(activity, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
}
}
@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
switch (requestCode) {
case 1: {
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
} else {
ToastUtil.showLongToast("需要SD卡許可權才可以使用此功能");
}
return;
}
}
}
@Override
public void onBackPressed() {
finish();
}
/**
* 註冊當音量發生變化時接收的廣播
*/
private void registerVolumeReceiver() {
mVolumeReceiver = new MyVolumeReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction("android.media.VOLUME_CHANGED_ACTION");
registerReceiver(mVolumeReceiver, filter);
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
Log.i(TAG, "surfaceCreated..");
try {
int CammeraIndex = findBackCamera();
Log.i(TAG, "BackCamera: " + CammeraIndex);
if (CammeraIndex == -1) {
CammeraIndex = findFrontCamera();
currentCameraType = FRONT;
switchCameraIV.setEnabled(false);
if (CammeraIndex == -1) {
Log.i(TAG, "NO camera!!");
return;
}
} else {
currentCameraType = BACK;
}
if (mCamera == null) {
mCamera = openCamera(currentCameraType);
}
} catch (Exception e) {
e.printStackTrace();
}
}
@SuppressLint("NewApi")
private Camera openCamera(int type) {
int frontIndex = -1;
int backIndex = -1;
int cameraCount = Camera.getNumberOfCameras();
Log.i(TAG, "cameraCount: " + cameraCount);
Camera.CameraInfo info = new Camera.CameraInfo();
for (int cameraIndex = 0; cameraIndex < cameraCount; cameraIndex++) {
Camera.getCameraInfo(cameraIndex, info);
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
frontIndex = cameraIndex;
} else if (info.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {
backIndex = cameraIndex;
}
}
currentCameraType = type;
if (type == FRONT && frontIndex != -1) {
curCameraIndex = frontIndex;
return Camera.open(frontIndex);
} else if (type == BACK && backIndex != -1) {
curCameraIndex = backIndex;
return Camera.open(backIndex);
}
return null;
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
Log.i(TAG, "surfaceChanged..");
initCamera(holder);
mCamera.cancelAutoFocus();// 只有加上了這一句,才會自動對焦
doAutoFocus();
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
//Check if it has back camera
private int findBackCamera() {
int cameraCount = 0;
Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
cameraCount = Camera.getNumberOfCameras();
for (int camIdx = 0; camIdx < cameraCount; camIdx++) {
Camera.getCameraInfo(camIdx, cameraInfo);
if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {
return camIdx;
}
}
return -1;
}
//監聽
//Check if it has front camera
private int findFrontCamera() {
int cameraCount = 0;
Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
cameraCount = Camera.getNumberOfCameras();
for (int camIdx = 0; camIdx < cameraCount; camIdx++) {
Camera.getCameraInfo(camIdx, cameraInfo);
if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
return camIdx;
}
}
return -1;
}
/*it will call when surfaceChanged*/
private void initCamera(SurfaceHolder holder) {
Log.i(TAG, "initCamera..");
if (mPreviewRunning) {
mCamera.stopPreview();
}
Camera.Parameters parameters;
try {
parameters = mCamera.getParameters();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
return;
}
parameters.setPreviewSize(videoWidth, videoHight);
parameters.setPictureFormat(PixelFormat.JPEG);
parameters.setPreviewFormat(PixelFormat.YCbCr_420_SP);
SetCameraFPS(parameters);
setCameraDisplayOrientation(this, curCameraIndex, mCamera);
mCamera.setParameters(parameters);
int bufferSize = (((videoWidth | 0xf) + 1) * videoHight * ImageFormat.getBitsPerPixel(parameters.getPreviewFormat())) / 8;
mCamera.addCallbackBuffer(new byte[bufferSize]);
mCamera.setPreviewCallbackWithBuffer(this);
try {
mCamera.setPreviewDisplay(holder);
} catch (Exception ex) {
// TODO Auto-generated catch block
if (null != mCamera) {
mCamera.release();
mCamera = null;
}
ex.printStackTrace();
}
mCamera.startPreview();
mCamera.autoFocus(myAutoFocusCallback);
mCamera.cancelAutoFocus();
mPreviewRunning = true;
}
//設定相機顯示的方向
private void setCameraDisplayOrientation(Activity activity, int cameraId, Camera camera) {
Camera.CameraInfo info = new Camera.CameraInfo();
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;
} else {
// back-facing
result = (info.orientation - degrees + 360) % 360;
}
Log.i(TAG, "curDegree: " + result);
camera.setDisplayOrientation(result);
}
private void SetCameraFPS(Camera.Parameters parameters) {
if (parameters == null) {
return;
}
int[] findRange = null;
int defFPS = 20 * 1000;
List<int[]> fpsList = parameters.getSupportedPreviewFpsRange();
if (fpsList != null && fpsList.size() > 0) {
for (int i = 0; i < fpsList.size(); ++i) {
int[] range = fpsList.get(i);
if (range != null
&& Camera.Parameters.PREVIEW_FPS_MIN_INDEX < range.length
&& Camera.Parameters.PREVIEW_FPS_MAX_INDEX < range.length) {
Log.i(TAG, "Camera index:" + i + " support min fps:" + range[Camera.Parameters.PREVIEW_FPS_MIN_INDEX]);
if (findRange == null) {
if (defFPS <= range[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]) {
findRange = range;
Log.i(TAG, "Camera found appropriate fps, min fps:" + range[Camera.Parameters.PREVIEW_FPS_MIN_INDEX]
+ " ,max fps:" + range[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
}
}
}
}
}
if (findRange != null) {
parameters.setPreviewFpsRange(findRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX], findRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
}
}
@Override
public void onPreviewFrame(byte[] data, Camera camera) {
frameCount++;
if (frameCount % 3000 == 0) {
Log.i("OnPre", "gc+");
System.gc();
Log.i("OnPre", "gc-");
}
if (data == null) {
Camera.Parameters params = camera.getParameters();
Camera.Size size = params.getPreviewSize();
int bufferSize = (((size.width | 0x1f) + 1) * size.height * ImageFormat.getBitsPerPixel(params.getPreviewFormat())) / 8;
camera.addCallbackBuffer(new byte[bufferSize]);
} else {
camera.addCallbackBuffer(data);
}
}
/**
* 處理音量變化時的介面顯示
*
* @author long
*/
private class MyVolumeReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals("android.media.VOLUME_CHANGED_ACTION")) {
if (getCurrentVolume() == 0) {
voiceIV.setBackgroundResource(R.drawable.surveillance_record_mute);
} else {
voiceIV.setBackgroundResource(R.drawable.surveillance_record_voice);
}
}
}
}
@Override
protected void onResume() {
super.onResume();
}
@Override
protected void onStop() {
super.onStop();
}
/* 建立保存錄製得到的視訊檔案
*
* @return
* @throws IOException
*/
private File createMediaFile() throws IOException {
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_MOVIES), "CameraDemo");
if (!mediaStorageDir.exists()) {
if (!mediaStorageDir.mkdirs()) {
Log.d(TAG, "failed to create directory");
return null;
}
}
// Create an image file name
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String imageFileName = "VID_" + timeStamp;
String suffix = ".mp4";
File mediaFile = new File(mediaStorageDir + File.separator + imageFileName + suffix);
return mediaFile;
} else {
// 當前不可用
}
return null;
}
//設定聚焦
private void doAutoFocus() {
parameters = mCamera.getParameters();
parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
mCamera.setParameters(parameters);
mCamera.autoFocus(new Camera.AutoFocusCallback() {
@Override
public void onAutoFocus(boolean success, Camera camera) {
if (success) {
camera.cancelAutoFocus();// 只有加上了這一句,才會自動對焦。
if (!Build.MODEL.equals("KORIDY H30")) {
parameters = camera.getParameters();
parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);// 連續自動對焦
camera.setParameters(parameters);
} else {
parameters = camera.getParameters();
parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
camera.setParameters(parameters);
}
}
}
});
}
}
裡面引用的庫不必關心,參考裡面攝像頭部分的流程和邏輯即可。