關於使用Android新版Camera即Camera2的使用介紹 暨解決Camera.PreviewCallback和MediaRecorder無法同時進行

來源:互聯網
上載者:User

標籤:

  新的相機API也就是Camera2是在Android 5.0引進的。通常情況下,我們都是使用Android舊的相機API,縱然在Android Studio裡老是提示已經廢棄,但是只要都能用,也就沒必要單獨為了使用新的API而寫兩套代碼。那為什麼要介紹Camera2的使用呢?一切問題的根源都是多樣化的需求引起的,特別是在Android領域,相容性問題更是層出不窮。經常會碰到,其他手機都可以,怎麼就這個不行……

  我也是跟大家一樣,碰到了一個跟相機有關的相容性問題。我們APP在進行活體識別的時候,除了要進行每個frame的檢測同時也要進行當前活體檢測的視頻錄製,使用的都是舊的相機API。在多樣化機型測試下,我們發現在紅米Note2和魅族MX5下,無法正常的同時進行活體檢測和視頻錄製,換得更技術一點的說法就是,在舊的API下,Camera.PreviewCallback和MediaRecorder不能同時進行。怎麼辦?google 來波search,你會發現,然並卵……剛開始,我們還專門聯絡了魅族的相機開發人員,以為會有什麼比較“魅族化”的方案,結果他們直接回了一句:平台相關,MX5不支援錄影輸出的同時提供預覽資料。怎麼辦?砍需求?這種關鍵流程,都是經過法務部門專門審核過的,那能說砍就砍。我們一邊申請是否可以砍掉這個需求,同時也依然繼續研究怎麼解決這個問題~

  我們發現這兩款不能同時進行的手機都是5.0以上的,於是我就想,也許新的Camera2有可能解決的~下面開始進行專業技術乾貨解說模式……

  當我們要學著使用某個新的API時,最好是直接到官網去找reference,然後盡量科學上網。Android的大部分API樣本,都在https://github.com/googlesamples裡面,這次提到的關於Camera2的使用,當然也是從那裡下載下來的,源碼地址如下:

https://github.com/googlesamples/android-Camera2Basic以及https://github.com/googlesamples/android-Camera2Video。但是googlesamples裡面的代碼都是比較原始的代碼。

  我們需要靜下心來分析相機使用的過程:

  1、首先一定得判斷許可權,是否有權利使用相機;

  2、通過什麼方式連上相機裝置;

  3、拿到相機裝置後怎麼進行錄影;

  4、如何在錄影的過程中監聽到每一幀的資料;

  

  一、關於檢查許可權就不說了,這裡補充一句有一個類叫ActivityCompat,大家如果以前沒用過可以看一下,是v4包下的類。

  二、Camera2開啟相機裝置的方式跟老的不一樣,以前直接就new一個就open了,比較直接。現在把camera當做一種服務去對待,要申請,而申請的方式如下,

private void openCamera() {        if (isOpened) {            return;        }        isOpened = true;        CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);        try {            String cameraId = manager.getCameraIdList()[0];//這個可能會有很多個,但是通常都是兩個,第一個是後置,第二個是前置;            CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);            StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);            assert map != null;            imageDimension = map.getOutputSizes(SurfaceTexture.class)[0];            manager.openCamera(cameraId, new CameraDevice.StateCallback() {                @Override                public void onOpened(CameraDevice camera) {                    G.i("onOpened");                    createCameraPreview(camera);                }                @Override                public void onDisconnected(CameraDevice camera) {                    G.i("onDisconnected");                    camera.close();                }                @Override                public void onError(CameraDevice camera, int error) {                    G.e("onError -> " + error);                    camera.close();                }            }, handlerHelper.getBackgroundHandler());//這個指定其後台運行,如果直接UI線程也可以,直接填null;            G.i("open Camera " + cameraId);        } catch (CameraAccessException e) {            e.printStackTrace();        }    }

  三、拿到相機裝置的回調就是如上代碼的 public void onOpened(CameraDevice camera) 方法,此時的cameraDevice就是我們可以完全使用的Camera。拿到相機以後,就開始建立預覽即preview。

protected void createCameraPreview(final CameraDevice cameraDevice) {        try {            if (null == cameraDevice) {                G.i("updatePreview error, return");                return;            }            setUpImageReader();            setUpMediaRecorder();            final CaptureRequest.Builder captureRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);            SurfaceTexture texture = textureView.getSurfaceTexture();            texture.setDefaultBufferSize(imageDimension.getWidth(), imageDimension.getHeight());            Surface textureSurface = new Surface(texture);            Surface recorderSurface = mMediaRecorder.getSurface();            Surface imageSurface = imageReader.getSurface();            captureRequestBuilder.addTarget(textureSurface);            captureRequestBuilder.addTarget(recorderSurface);            captureRequestBuilder.addTarget(imageSurface);            List<Surface> surfaceList = Arrays.asList(textureSurface, recorderSurface, imageSurface);            cameraDevice.createCaptureSession(surfaceList, new CameraCaptureSession.StateCallback() {//配置要接受映像的surface                @Override                public void onConfigured(CameraCaptureSession cameraCaptureSession) {                    captureRequestBuilder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO);                    try {                        cameraCaptureSession.setRepeatingRequest(captureRequestBuilder.build(), null, handlerHelper.getBackgroundHandler());//成功配置後,便開始進行相機映像的監聽                    } catch (CameraAccessException e) {                        e.printStackTrace();                    }                    mMediaRecorder.start();                }                @Override                public void onConfigureFailed(CameraCaptureSession cameraCaptureSession) {                    ToastUtils.show("Configuration change");                }            }, handlerHelper.getBackgroundHandler());        } catch (CameraAccessException e) {            e.printStackTrace();        }    }    private void setUpMediaRecorder() {        mMediaRecorder = new MediaRecorder();        mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT);        mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);        mMediaRecorder.setProfile(getCamcorderProfile());        mMediaRecorder.setOutputFile(new File(getExternalCacheDir(), System.currentTimeMillis() + ".mp4").getAbsolutePath());        try {            mMediaRecorder.prepare();        } catch (IOException e) {            e.printStackTrace();        }    }    private void setUpImageReader() {        imageReader = ImageReader.newInstance(imageDimension.getWidth(), imageDimension.getHeight(), ImageFormat.YUV_420_888, 10);        imageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() {            @Override            public void onImageAvailable(ImageReader reader) {                Image image = reader.acquireLatestImage();                if (image != null) {                    image.close();                }                G.i("onImageAvailable");            }        }, handlerHelper.getBackgroundHandler());    }

由於camera2的api使用的方式是,相機裝置可以向意多個surface進行映像的輸出。如上代碼,通過cameraDevice.createCaptureSession(....)方法去配置要輸出的surface,任意一個沒有被配置過的surface在使用的時候都會報錯。同時有回調通知,是否配置成功,成功以後便可以開始啟動映像的輸出監聽,即cameraCaptureSession.setRepeatingRequest(captureRequestBuilder.build(), null, handlerHelper.getBackgroundHandler());其中captureRequestBuilder就是配置相機屬性以及添加那些已經成功配置過了surface,至於怎麼接收相機的映像,便有各個surface的所有者自己去定義。這裡使用到的是MediaRecorder和ImageReader,一個是為了錄影,一個是為了所謂的監聽PreviewCallback。

  注意:使用ImageReader的時候會比較卡,特別是如果使用JPEG的格式的話,因為使用JPEG,ImageReader需要進行額外的處理。我為了使回調與舊的PreviewCallback一樣使用了ImageFormat.YUV_420_888格式。這個格式,輸出非常流暢。

 

  

 

 

關於使用Android新版Camera即Camera2的使用介紹 暨解決Camera.PreviewCallback和MediaRecorder無法同時進行

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.