Android Camera2入門系列1 - Camera2在textureView預(yù)覽

Android Camera2入門

Android Camera2入門系列1 - Camera2在textureView預(yù)覽
Android Camera2入門系列2 - ImageReader獲得預(yù)覽數(shù)據(jù)
Android Camera2入門系列3 - Image中獲得YUV數(shù)據(jù)及YUV格式理解
Android Camera2入門系列4 - libyuv的編譯和使用

本文提供最easy的Camera2的入門,供入門查看,意在精簡(jiǎn),深入內(nèi)容看后續(xù)文章。

The android.hardware.camera2 package provides an interface to individual camera devices connected to an Android device. It replaces the deprecated [Camera] class.
camera2給Android的給個(gè)Camera設(shè)備提供了接口,并且deprecated掉了Camera類

下面是翻譯了部分Camera2的API描述:基本上涉及了我們使用camera2的所有的API:
通過(guò)CameraManager能查詢本設(shè)備有多少個(gè)available的Camera設(shè)備。
每個(gè)CameraDevice設(shè)備提供了一系列靜態(tài)參數(shù)去描述當(dāng)前的Camera設(shè)備,比如設(shè)置或者輸出參數(shù),這些參數(shù)通過(guò)[CameraCharacteristics]提供出來(lái),通過(guò)[getCameraCharacteristics(cameraId)]獲取。
從相機(jī)設(shè)備獲取一個(gè)或者多個(gè)image,首先必須創(chuàng)建一個(gè)CameraCaptureSession并輸出到一個(gè)或多個(gè)目標(biāo)Surface上。每個(gè)Surface必須預(yù)先設(shè)置合適的預(yù)覽尺寸,這個(gè)尺寸必須是Camera支持的尺寸。目標(biāo)Surface可以被一系列的類所持有,比如SurfaceView,SurfaceTexture,MediaCodec,MediaRecorder,AllocationImageReader。也就是說(shuō)Camera的輸出可以被分發(fā)到多個(gè)Surface上面。
通常,相機(jī)預(yù)覽圖像可以被發(fā)送到SurfaceView或者TextureView上面,像拍照的時(shí)候去單獨(dú)獲取某一幀或者特效相機(jī)類的App獲得要處理的RAW數(shù)據(jù)流可以通過(guò)ImageReader來(lái)獲取JPEG格式或者YUV格式的圖像數(shù)據(jù)。比如要用RenderScript, OpenGL ES或者直接在native處理的數(shù)據(jù)就推薦使用YUV_420_888數(shù)據(jù)格式來(lái)承載。
如果相機(jī)設(shè)備要獲取Image(也就是獲取圖像的raw數(shù)據(jù):JPEG或者YUV數(shù)據(jù)),我們需要?jiǎng)?chuàng)建一個(gè)定義了相機(jī)需要的參數(shù)的CaptureRequest,CameraDevice有工廠方法去創(chuàng)建一個(gè)request builder
一旦request被創(chuàng)建出來(lái),它可以被一個(gè)active狀態(tài)的session拿去得到一個(gè)Image(one-shot)或者多個(gè)Image(endless),也就是說(shuō)session通過(guò)request去得到一張圖或者多張圖。

//得到一個(gè)Image
session.capture(request, null, mCameraHandler);
//一直回調(diào)返回Image
session.setRepeatingRequest(request, null, mCameraHandler);

API使用流程大體如下:

  1. 通過(guò)context.getSystemService(Context.CAMERA_SERVICE) 獲取CameraManager.
  2. 調(diào)用CameraManager .open()方法在回調(diào)中得到CameraDevice.
  3. 通過(guò)CameraDevice.createCaptureSession() 在回調(diào)中獲取CameraCaptureSession.
  4. 構(gòu)建CaptureRequest, 有三種模式可選 預(yù)覽/拍照/錄像.
  5. 通過(guò) CameraCaptureSession發(fā)送CaptureRequest, capture表示只發(fā)一次請(qǐng)求, setRepeatingRequest表示不斷發(fā)送請(qǐng)求.
  6. 拍照數(shù)據(jù)可以在ImageReader.OnImageAvailableListener回調(diào)中獲取, CaptureCallback中則可獲取拍照實(shí)際的參數(shù)和Camera當(dāng)前狀態(tài).
Camera2流程圖

上文提到了Camera2的某些必要的API。其實(shí)單純的去看某個(gè)類或某些類的職責(zé),只是看這些API的功能描述只會(huì)看的頭大,因?yàn)閏amera2確實(shí)提供了很多API來(lái)控制。還不如直接來(lái)看一個(gè)demo來(lái)的更加直觀,推薦大家不要拷貝,哪怕看著自己敲一遍,印象會(huì)深刻很多。

public class Camera2Provider {
    private Activity mContext;
    private String mCameraId;
    private Handler mCameraHandler;
    private CameraDevice mCameraDevice;
    private TextureView mTextureView;
    private CaptureRequest.Builder mPreviewBuilder;
    private Size previewSize;

    public Camera2Provider(Activity mContext) {
        this.mContext = mContext;
        //創(chuàng)建了一個(gè)Thread來(lái)供Camera運(yùn)行使用,使用HandlerThread而不使用Thread是因?yàn)镠andlerThread給我們創(chuàng)建了Looper,不用我們自己創(chuàng)建了。
        HandlerThread handlerThread = new HandlerThread("camera");
        handlerThread.start();
        mCameraHandler = new Handler(handlerThread.getLooper());
    }
    /**
     * 設(shè)置預(yù)覽view
     *
     * @param textureView 需要預(yù)覽的TextureView
     */
    public void initTexture(TextureView textureView) {
        mTextureView = textureView;
        textureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
            @Override
            public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
                openCamera(width, height);
            }
            @Override
            public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {}
            @Override
            public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {return false;}
            @Override
            public void onSurfaceTextureUpdated(SurfaceTexture surface) {}
        });
    }

    /**
     * surface ready的時(shí)候開(kāi)啟Camera
     *
     * @param width  surface的寬
     * @param height surface的高
     */
    private void openCamera(int width, int height) {
        CameraManager cameraManager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
        try {
            for (String cameraId : cameraManager.getCameraIdList()) {
                //描述相機(jī)設(shè)備的屬性類
                CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics(cameraId);
                //獲取是前置還是后置攝像頭
                Integer facing = characteristics.get(CameraCharacteristics.LENS_FACING);
                //使用后置攝像頭
                if (facing != null && facing == CameraCharacteristics.LENS_FACING_BACK) {
                    StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
                    if (map != null) {
                        previewSize = CameraUtil.getOptimalSize(map.getOutputSizes(SurfaceTexture.class), width, height);
                        mCameraId = cameraId;
                    }
                }
            }
            String[] params = new String[]{Manifest.permission.CAMERA};
            if (!PermissionUtil.checkPermission(mContext, params)) {
                PermissionUtil.requestPermission(mContext, "", 0, params);
            }
            cameraManager.openCamera(mCameraId, mStateCallback, mCameraHandler);
        } catch (CameraAccessException r) {}
    }

    /**
     * 狀態(tài)回調(diào)
     */
    private CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() {
        @Override
        public void onOpened(CameraDevice camera) {
            mCameraDevice = camera;
            SurfaceTexture surfaceTexture = mTextureView.getSurfaceTexture();
            surfaceTexture.setDefaultBufferSize(previewSize.getWidth(), previewSize.getHeight());
            Surface previewSurface = new Surface(surfaceTexture);
            try {
                mPreviewBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
                //如果需要多個(gè)surface可以add多個(gè)
                mPreviewBuilder.addTarget(previewSurface);
                mCameraDevice.createCaptureSession(Arrays.asList(previewSurface), mStateCallBack, mCameraHandler);
            } catch (CameraAccessException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onDisconnected(CameraDevice camera) {
            camera.close();
        }

        @Override
        public void onError(CameraDevice camera, int error) {
            camera.close();
        }
    };

    private CameraCaptureSession.StateCallback mStateCallBack = new CameraCaptureSession.StateCallback() {
        @Override
        public void onConfigured(CameraCaptureSession session) {
            CaptureRequest request = mPreviewBuilder.build();
            try {
                //獲取一個(gè)Image,one-shot
//                session.capture(request, null, mCameraHandler);
                //開(kāi)啟獲取Image,repeat模式
                session.setRepeatingRequest(request, null, mCameraHandler);
            } catch (CameraAccessException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onConfigureFailed(CameraCaptureSession session) {}
    };
    /**
     * 記得關(guān)掉Camera
     */
    public void closeCamera() {
        mCameraDevice.close();
    }
}

需要注意的幾個(gè)問(wèn)題:

  1. 記得添加權(quán)限,并自己實(shí)現(xiàn)動(dòng)態(tài)請(qǐng)求權(quán)限。
  2. 創(chuàng)建一個(gè)Thread供Camera2使用。
  3. 注意預(yù)覽尺寸和Camera能提供的尺寸。

github代碼:Camera2Provider.java 歡迎star/follow

接著看下一篇如何獲取回調(diào)的數(shù)據(jù)Android Camera系列2 - ImageReader獲得預(yù)覽數(shù)據(jù)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容