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的入門,供入門查看,意在精簡荤傲,深入內(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:
通過CameraManager能查詢本設(shè)備有多少個(gè)available的Camera設(shè)備瘟檩。
每個(gè)CameraDevice設(shè)備提供了一系列靜態(tài)參數(shù)去描述當(dāng)前的Camera設(shè)備,比如設(shè)置或者輸出參數(shù)澈蟆,這些參數(shù)通過[CameraCharacteristics]提供出來墨辛,通過[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也就是說Camera的輸出可以被分發(fā)到多個(gè)Surface上面橙垢。
通常垛叨,相機(jī)預(yù)覽圖像可以被發(fā)送到SurfaceView或者TextureView上面,像拍照的時(shí)候去單獨(dú)獲取某一幀或者特效相機(jī)類的App獲得要處理的RAW數(shù)據(jù)流可以通過ImageReader來獲取JPEG格式或者YUV格式的圖像數(shù)據(jù)柜某。比如要用RenderScript, OpenGL ES或者直接在native處理的數(shù)據(jù)就推薦使用YUV_420_888數(shù)據(jù)格式來承載嗽元。
如果相機(jī)設(shè)備要獲取Image(也就是獲取圖像的raw數(shù)據(jù):JPEG或者YUV數(shù)據(jù)),我們需要創(chuàng)建一個(gè)定義了相機(jī)需要的參數(shù)的CaptureRequest喂击,CameraDevice有工廠方法去創(chuàng)建一個(gè)request builder剂癌。
一旦request被創(chuàng)建出來,它可以被一個(gè)active狀態(tài)的session拿去得到一個(gè)Image(one-shot)或者多個(gè)Image(endless)翰绊,也就是說session通過request去得到一張圖或者多張圖佩谷。

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

API使用流程大體如下:

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

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

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來供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í)候開啟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);
                //開啟獲取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è)問題:

  1. 記得添加權(quán)限放仗,并自己實(shí)現(xiàn)動態(tài)請求權(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)容合作請聯(lián)系作者
  • 序言:七十年代末莉撇,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子惶傻,更是在濱河造成了極大的恐慌棍郎,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,682評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件银室,死亡現(xiàn)場離奇詭異涂佃,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)蜈敢,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評論 3 395
  • 文/潘曉璐 我一進(jìn)店門辜荠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人抓狭,你說我怎么就攤上這事伯病。” “怎么了否过?”我有些...
    開封第一講書人閱讀 165,083評論 0 355
  • 文/不壞的土叔 我叫張陵午笛,是天一觀的道長。 經(jīng)常有香客問我苗桂,道長药磺,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,763評論 1 295
  • 正文 為了忘掉前任煤伟,我火速辦了婚禮癌佩,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘持偏。我一直安慰自己驼卖,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,785評論 6 392
  • 文/花漫 我一把揭開白布鸿秆。 她就那樣靜靜地躺著酌畜,像睡著了一般。 火紅的嫁衣襯著肌膚如雪卿叽。 梳的紋絲不亂的頭發(fā)上桥胞,一...
    開封第一講書人閱讀 51,624評論 1 305
  • 那天,我揣著相機(jī)與錄音考婴,去河邊找鬼贩虾。 笑死,一個(gè)胖子當(dāng)著我的面吹牛沥阱,可吹牛的內(nèi)容都是我干的缎罢。 我是一名探鬼主播,決...
    沈念sama閱讀 40,358評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼策精!你這毒婦竟也來了舰始?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,261評論 0 276
  • 序言:老撾萬榮一對情侶失蹤咽袜,失蹤者是張志新(化名)和其女友劉穎丸卷,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體询刹,經(jīng)...
    沈念sama閱讀 45,722評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡谜嫉,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了凹联。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片沐兰。...
    茶點(diǎn)故事閱讀 40,030評論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖匕垫,靈堂內(nèi)的尸體忽然破棺而出僧鲁,到底是詐尸還是另有隱情,我是刑警寧澤象泵,帶...
    沈念sama閱讀 35,737評論 5 346
  • 正文 年R本政府宣布寞秃,位于F島的核電站,受9級特大地震影響偶惠,放射性物質(zhì)發(fā)生泄漏春寿。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,360評論 3 330
  • 文/蒙蒙 一忽孽、第九天 我趴在偏房一處隱蔽的房頂上張望绑改。 院中可真熱鬧,春花似錦兄一、人聲如沸厘线。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽造壮。三九已至,卻和暖如春骂束,著一層夾襖步出監(jiān)牢的瞬間耳璧,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評論 1 270
  • 我被黑心中介騙來泰國打工展箱, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留旨枯,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,237評論 3 371
  • 正文 我出身青樓混驰,卻偏偏與公主長得像攀隔,于是被迫代替她去往敵國和親皂贩。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,976評論 2 355