利用Android Camera2 的照相机api 实现 实时的图像采集与预览
? ?最近想要做一個客戶端往服務(wù)器推送實時畫面的功能,首先可以考慮到兩種思路,一種是在客戶端進行視頻流的推送,主要利用RTSP等流媒體協(xié)議進行傳輸,而另外一種是通過攝像頭獲取當(dāng)前畫面,將每一幀作為對象單獨傳輸。
? ?項目想要實現(xiàn)的功能最終目的是對實時畫面的每一幀進行處理,可以考慮客戶端推流到服務(wù)器,再在服務(wù)器進行幀解析的操作,但由于目前很多的流媒體推送框架在推流端或者服務(wù)端都或多或少存在限制,很少有完全開源的項目,再加上傳送畫面的同時需要附帶部分的數(shù)據(jù),仍然需要另外建立連接進行傳輸,所以暫時擱置這一方案。選擇第二種思路,獲取每一幀的畫面,單獨傳輸。
? ?要想獲取實時畫面,我們必須通過對安卓設(shè)備上的攝像頭進行調(diào)用。
? ?從API21開始,安卓引入了android.hardware.camera2這個包,來替代原有的camera類,原有的camera類已經(jīng)不再建議使用了。camera2中最重要的變化是,攝像頭的調(diào)用不再是簡單地進行實例化,而是用一種類似服務(wù)申請的方式來進行調(diào)用。通過CameraManager來管理攝像服務(wù),需要通過建立CameraCaptureSession來建立一個調(diào)用攝像設(shè)備CameraDevices的會話,來實現(xiàn)對攝像頭的調(diào)用。而CaptureRequest.Builder類用于建立實際的調(diào)用請求,具體的參數(shù)設(shè)置也可以通過這個類來實現(xiàn)(而不是對camera設(shè)備進行直接設(shè)置),這樣做的目的是把對攝像頭的控制與攝像頭本身分離開來,用戶可以通過不同的session根據(jù)不同的配置來使用攝像頭。
? ?我們可以結(jié)合具體的代碼來分析新api中攝像頭調(diào)用的過程。
? ?首先我們想要對攝像設(shè)備進行操作,需要獲得CameraManager的實例
? ?我們可以調(diào)用openCamera函數(shù)打開攝像頭設(shè)備
????cameraManager.openCamera(cameraId,?cameraCallback,?mainHandler);? ?這里需要傳入三個參數(shù),cameraId是設(shè)備編號,cameraCallback控制攝像服務(wù)的回調(diào),最后一個參數(shù)指定HandlerThread對象?
?????cameraId?=?Integer.toString(CameraCharacteristics.LENS_FACING_FRONT);private?CameraDevice.StateCallback?cameraCallback?=?new?CameraDevice.StateCallback()?{@Overridepublic?void?onOpened(CameraDevice?camera)?{Log.d("CameraCallback",?"Camera?Opened");cameraDevice?=?camera;takePreview();}@Overridepublic?void?onDisconnected(CameraDevice?cameraDevice)?{Log.d("CameraCallback",?"Camera?Disconnected");closeCameraDevice();}@Overridepublic?void?onError(CameraDevice?cameraDevice,?int?i)?{Log.d("CameraCallback",?"Camera?Error");Toast.makeText(PusherSurface.this,?"攝像頭開啟失敗",?Toast.LENGTH_SHORT).show();}};?回調(diào)函數(shù)用于指定連接攝像頭設(shè)備時不同狀態(tài)的操作。在這里,我們在攝像頭成功連接的時候調(diào)用 ?takePreview()函數(shù)開啟攝像頭畫面的預(yù)覽。
要從攝像設(shè)備中獲取圖像,我們首先需要建立一個camera capture session。函數(shù)
createCaptureSession(List, CameraCaptureSession.StateCallback, Handler)的第一個參數(shù)傳入了我們想要繪制的視圖列表,第二個參數(shù)傳入的是建立攝像會話的狀態(tài)回調(diào)函數(shù),第三個參數(shù)傳入相應(yīng)的handler處理器。然后,我們需要利用capturerequest來定義攝像頭捕獲圖像時候的具體參數(shù),比如是否開啟攝像頭,是否自動對焦等。最后通過CamraCaptureSession.setRepeatingRequest來開啟請求。這樣我們就可以從capturesession傳入的list中的surface列表獲得連續(xù)的圖像。留意到
previewRequestBuilder.addTarget(surfaceHolder.getSurface()); previewRequestBuilder.addTarget(previewReader.getSurface());這里除了傳入xml界面布局中的surfaceHolder的surface外,還傳入了一個previewReader的surface。
previewReader是一個自定義的ImageReader對象。
previewReader?=?ImageReader.newInstance(1080,?1920,?ImageFormat.YUV_420_888,?2);previewReader.setOnImageAvailableListener(new?ImageReader.OnImageAvailableListener()?{@Overridepublic?void?onImageAvailable(ImageReader?p_w_picpathReader)?{Image?p_w_picpath?=?null;try?{p_w_picpath?=?p_w_picpathReader.acquireLatestImage();Log.d("PreviewListener",?"GetPreviewImage");if?(p_w_picpath?==?null)?{return;}byte[]?bytes?=?ImageUtil.p_w_picpathToByteArray(p_w_picpath);if?(pushFlag?==?false)uploadImg(bytes);}?finally?{if?(p_w_picpath?!=?null)?{p_w_picpath.close();}}}},?mainHandler);ImageReader是一個可以讓我們對繪制到surface的圖像進行直接操作的類。在這里我們從攝像設(shè)備中傳入了連續(xù)的預(yù)覽圖片,也就是我們在屏幕上看到的畫面,它們的格式都是未經(jīng)壓縮的YUV_420_888類型的(同樣的如果要操作拍攝后的圖片,就要設(shè)置成jpeg格式)。我們調(diào)用p_w_picpathReader.acquireLatestImage或者acquireNextImage來獲取圖像隊列中的圖片。并進行操作。在這里我利用一個函數(shù)將圖像壓縮后轉(zhuǎn)化成byte[]格式,并調(diào)用uploadImg函數(shù)上傳至服務(wù)器。這樣,整個攝像頭的調(diào)用到預(yù)覽圖像的處理也就完成了。想要實現(xiàn)拍照功能也是大同小異,在這里我就不一一貼出了。
??歡迎更多安卓開發(fā)者一同交流。
轉(zhuǎn)載于:https://blog.51cto.com/davidwillo/1908166
總結(jié)
以上是生活随笔為你收集整理的利用Android Camera2 的照相机api 实现 实时的图像采集与预览的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: CCF计算机职业资格认证2016-12-
- 下一篇: springmvc的执行流程详解