Android 是時候使用Camera2的時候了(當Camera無介面不能預覽)
Camera2的bug:
error1:
LegacyCameraDevice_nativeGetSurfaceId: Could not retrieve native Surface from surface.
error2:
getNativeWindow: Surface had no valid native window.
LegacyCameraDevice_nativeDetectSurfaceType: Could not retrieve native window from surface.
使用Camera一般都是用ImageReader來匯出frame來進行處理,這兩個錯誤都是由於Camera使用不當造成的。
反覆測試發現,如果關閉Camera2之前沒有stoprepeating,就會報上面的錯誤,果斷,先stopReapting再關閉相機,示例程式碼如下(完整程式碼可以參考google官方示例程式碼的基礎上,改進而來,下面有連結):
public void stop() {
try {
mCameraCaptureSession.stopRepeating();
} catch (CameraAccessException e) {
e.printStackTrace();
}
mBackgroundHandler.postDelayed(new Runnable() {//延遲關閉相機
@Override
public void run() {
if (mCameraCaptureSession!=null){
mCameraCaptureSession.close();
mCameraCaptureSession = null;
}
if (mCameraDevice!=null)//注意關閉順序,先
mCameraDevice.close();
mCameraDevice = null;
if (mImageReader!=null )//注意關閉順序,後
mImageReader.close();
mImageReader = null;
isCameraOn = false;
stopBackgroundThread();
}
},100);
}
上面是開胃菜,下面繼續
先聽聽我的故事:
最近做人臉識別,都是別人實現的庫,直接呼叫就好了,結果被Camera坑。
Camera預覽貞正常是前提,在android5.1上執行好好的,到Android6.0上就是預覽不被回撥(經過多方比較,就是少了一個設定setPreviewTexture(因為我應用不需要這個,沒有介面),具體原因不詳);使用Camera2正常所以才有了後面的事
通過Camera2,預覽中把人臉框出來:opencv人臉框出來
先看看api文件:官方文件
最好的例子:官方sample地址
使用場景:
1、無介面預覽(有surfaceview)
只取預覽圖片,對圖片特徵進行處理(如,人臉識別),不需要將圖片顯示出來
注意:a、圖片的格式ImageFormat(如果用imagereader 請使用YUV_420_888)
2、有介面預覽(無surfaceview)
需要預覽圖片,並將結果顯示在surfaceview,textureview等控制元件上。
3、獲取Camera支援的解析度(我是遇到172*144,相機並不支援這個解析度,我強行設定的結果是實際輸出的解析度是176*144,表現出來的bug就是圖片顯示出來“花了”)
mCameraManager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
try {
characteristics = mCameraManager.getCameraCharacteristics("0");
listMap = characteristics.get( SCALER_STREAM_CONFIGURATION_MAP);
listSize = listMap.getOutputSizes(ImageFormat.JPEG);
} catch (CameraAccessException e) {
e.printStackTrace();
}
搜尋“camera2”
出來很多
- CaptureResult
- CameraCaptureSession
- CameraManager
- CameraCharacteristics
- CameraDevice
- CaptureRequest
CameraCaptureSession.StateCallback
Camera
import android.Manifest;
import android.app.Activity;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.ImageFormat;
import android.graphics.SurfaceTexture;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CaptureRequest;
import android.media.Image;
import android.media.ImageReader;
import android.os.Handler;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.Surface;
import android.view.TextureView;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.ImageView;
import java.nio.ByteBuffer;
import java.util.Arrays;
public class Camera2ActivityMine extends Activity implements TextureView.SurfaceTextureListener{
private final static String TAG = Camera2ActivityMine.class.getSimpleName();
TextureView mTexture;
CameraManager cameraManager;
CameraDevice mCameraDevice;
Handler mainHandler;
CaptureRequest.Builder captureRequestBuilder;
ImageReader mImageReader;
CameraCaptureSession mCameraCaptureSession;
ImageView mImageView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.activity_camera2_mine);
mTexture = (TextureView) findViewById(R.id.mine_texture);
mImageView = (ImageView)findViewById(R.id.mine_picture);
mImageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
takePicture();
}
});
mTexture.setSurfaceTextureListener(this);
}
public void init(){
cameraManager = (CameraManager)getSystemService(CAMERA_SERVICE);
mainHandler = new Handler(getMainLooper());
mImageReader = ImageReader.newInstance(mTexture.getWidth(),mTexture.getHeight(), ImageFormat.JPEG,2);
mImageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() {
@Override
public void onImageAvailable(ImageReader imageReader) {
// 拿到拍照照片資料
Image image = imageReader.acquireLatestImage();
ByteBuffer buffer = image.getPlanes()[0].getBuffer();
byte[] bytes = new byte[buffer.remaining()];
buffer.get(bytes);//由緩衝區存入位元組陣列
final Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
if (bitmap != null) {
mImageView.setImageBitmap(bitmap);
}
image.close();
}
}, mainHandler);
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA)!= PackageManager.PERMISSION_GRANTED){
return;
}
try {
cameraManager.openCamera(String.valueOf(CameraCharacteristics.LENS_FACING_FRONT), new CameraDevice.StateCallback() {
@Override
public void onOpened(@NonNull CameraDevice cameraDevice) {
mCameraDevice = cameraDevice;
afterOpenCamera();
}
@Override
public void onDisconnected(@NonNull CameraDevice cameraDevice) {
mCameraDevice.close();
}
@Override
public void onError(@NonNull CameraDevice cameraDevice, int i) {
Log.e(TAG,cameraDevice.toString()+i);
}
},mainHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int i, int i1) {
init();
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int i, int i1) {
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) {
return false;
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) {
}
//開啟攝像頭之後要做的事情
public void afterOpenCamera(){
SurfaceTexture surfaceTexture = mTexture.getSurfaceTexture();
surfaceTexture.setDefaultBufferSize(mTexture.getWidth(),mTexture.getHeight());
Surface surface = new Surface(surfaceTexture);
try {
captureRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
} catch (CameraAccessException e) {
e.printStackTrace();
}
captureRequestBuilder.addTarget(surface);
try {
mCameraDevice.createCaptureSession(Arrays.asList(surface,mImageReader.getSurface()), new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) {
try {
mCameraCaptureSession = cameraCaptureSession;
cameraCaptureSession.setRepeatingRequest(captureRequestBuilder.build(), new CameraCaptureSession.CaptureCallback() {
},mainHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
@Override
public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession) {
}
}, mainHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
//拍照
public void takePicture(){
captureRequestBuilder.addTarget(mImageReader.getSurface());
try {
mCameraCaptureSession.capture(captureRequestBuilder.build(),null,mainHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
}
layout
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:orientation="horizontal"
android:layout_height="match_parent">
<TextureView
android:id="@+id/mine_texture"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="match_parent" />
<ImageView
android:layout_weight="1"
android:id="@+id/mine_picture"
android:layout_width="0dp"
android:layout_height="match_parent" />
</LinearLayout>