1. 程式人生 > >Android 是時候使用Camera2的時候了(當Camera無介面不能預覽)

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>