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


  新的相機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格式。這個格式,輸出非常流暢。

 

  

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM