多媒體包括圖片、影片、音頻、視頻,這些多媒體素材的采集(輸入)主要依靠攝像頭和麥克風等硬體設備轉化為基礎資料,而他們的播放渲染(輸出),則需要依靠具有相關功能的編解碼軟體,當然隨著硬體集成度越來越高,也有些基礎功能內置到硬體中解碼,以此減少軟體解碼程序中的CPU耗時操作,這種方式稱為硬體加速,由于多媒體的播放渲染(輸出)是由系統主動向用戶發出的,通常不需要向用戶申請權限,系統將資料直接發給應用程式,進而在應用程式內編程實作相關資料的解碼播放渲染(輸出)操作,故文章重點介紹在多媒體采集(輸入)程序中可能用到的硬體及相關使用流程,
多媒體系列硬體
攝像頭及相關硬體
攝像頭作為移動手機設備的重要硬體之一,從最初的單一攝像頭,到最新的浴霸式四孔攝像頭,不管是數量,還是焦距性能上,在不同設備上都有不同的區別,與傳感器系列硬體互動一文相似的是,這些繁雜的型別,都由系統適配完成,而應用程式只需要使用系統提供的相關類即可,
對于攝像頭硬體的使用,在Android5.0即API級別21以下的系統版本中,可以使用android.hardware.Camera攝像頭類的相關方法來獲取攝像頭資料,以用來實時預覽攝像頭采集的資料、拍照保存某一時刻的資料、或錄制視頻保存一段時刻內的資料,但是從Android5.0開始,上述類由于過于臃腫而廢棄,進而使用android.hardware.camra2. 包下的相關類開發更定制化的應用,
權限宣告
對于使用攝像頭硬體的應用程式,都需要宣告權限為Manifest.permissions.CAMERA="android.permission.CAMERA",
同樣也可以在應用程式清單檔案中宣告需要攝像頭硬體的設備支持,也可以增加標簽資訊<uses-feature android:name="android.hardware.camera"/>,
另外,如果在使用攝像頭拍照時,需要在照片中保存位置資訊,應用程式需要申請位置權限;而想將照片存盤到外部存盤設備,還需要應用程式申請讀寫外部存盤的相關權限;如果是使用攝像頭錄制有聲視頻,應用程式還需要申請麥克風權限,
使用流程
目標版本為API 21以下
在使用前首先檢測攝像頭硬體,在能獲取到Context背景關系環境物件的位置,呼叫背景關系環境物件的getPackageManager()方法獲取android.content.pm.PackageManager包管理類的實體化物件,進而通過該物件的hasSystemFeature(String featureName)方法,使引數 featureName 值為PackageManager.FEATURE_CAMERA 代表攝像頭功能,來判斷當前系統是否有攝像頭硬體的支持,
對于有攝像頭硬體支持的設備,可以使用Camera.getNumberOfCameras()靜態方法獲取當前設備的所有可用攝像頭數量,而每個攝像頭硬體都對應一個int型別的 cameraId 屬性編號,其值大于等于0,且小于靜態方法獲取可用攝像頭數量,在下面獲取攝像頭資訊和打開指定攝像頭時均是根據 cameraId 屬性值確定的,
對于每一個具有 cameraId 屬性值的攝像頭,都可以呼叫Camera.open(int cameraId)方法獲取到對應的Camera攝像頭類的實體化物件,引數 cameraId 即上文提到的攝像頭硬體編號,該引數默認值為0;如果編號引數對應的攝像頭硬體不存在時,該方法則回傳空指標,
在得到Camera實體化物件后,可以查看該攝像頭硬體的詳細資訊,呼叫該物件的getParameters()方法,得到回傳值為android.hardware.Camera.Parameters攝像頭引數型別的物件,在Camera.Parameters引數型別的物件中,可以使用getX系列方法獲取包括閃光燈、聚焦、解析度等系列資訊;同時也可以使用setX系列方法重新調整設定包括閃光燈、聚焦、解析度等系列資訊,如果修改攝像頭硬體的引數物件后,可以呼叫Camera攝像頭物件的setParameters(Camera.Parameters params)方法,將修改后的引數應用到對應的攝像頭硬體中,
預覽
要實作Camera攝像頭的預覽功能,只需要借助自定義控制元件類,該類繼承自系統控制元件android.view.SurfaceView類,
在自定義控制元件類的構造方法中,傳入上文獲取的Camera攝像頭物件作為該類的全域變數,以供在預覽功能開啟或關閉時呼叫攝像頭物件的相關方法,
之后可以在自定義控制元件類內部呼叫自己的getHolder()方法,回傳android.view.SurfaceHolder型別的物件,該物件是系結當前SurfaceView控制元件與其中的控制資訊的,可以呼叫該物件的setX系列方法設定當前自定義的SurfaceView中的顯示資訊,同時呼叫該物件的addCallback(SurfaceHolder.Callback callback)方法為當前自定義SurfaceView增加界面更新的回呼,引數 callback 為回呼介面android.view.SurfaceHolder.Callback實作的實體化物件,
在SurfaceHolder.Callback介面的實體化物件中,分別實作surfaceCreated(SurfaceHolder holder)在自定義控制元件創建時回呼的方法,通常在該方法中呼叫當前類的全域變數Camera物件的setPreviewDisplay(holder)方法將攝像頭與當前控制元件系結,之后呼叫Camera物件的startPreview()方法啟動攝像頭的預覽,這樣攝像頭采集的資料就會實時展示在當前自定義SurfaceView控制元件中了;surfaceChanged(SurfaceHolder holder, int format, int width, int height)在自定義控制元件包括后邊三個引數所代表的資訊發生改變時回呼的方法,此時一般要先呼叫全域變數Camera物件的stopPreview()停止預覽,之后完成該控制元件內部的一些更新資訊,最后再重新呼叫Camera物件的setPreviewDisplay(holder)和startPreview()方法重新系結并啟動預覽;surfaceDestroyed(SurfaceHolder holder)在自定義控制元件被銷毀時回呼的方法,通常在剛方法中會呼叫Camera物件的stopPreview()停止預覽,最后呼叫release()方法直接釋放相關資源,這樣該Camera物件的相關資料便都清空了,
拍照
要實作攝像頭的拍照功能,只需要呼叫Camera物件的takePicture(Camera.ShutterCallback shutter, Camera.PictureCallback raw, Camera.PictureCallback postview, Camera.PictureCallback jpeg)方法,其中引數 shutter 是拍照那一刻回呼的android.hardware.Camera.ShutterCallback介面物件,在拍攝照片時,會回呼該物件的唯一方法onShutter(),因此如果想在拍照時搞些小動作,可以在該物件的onShutter()中添加代碼,通常該引數 shutter 為 null;
引數 raw 、 postview 、 jpeg 三個都是android.hardware.Camera.PictureCallback圖片回呼介面的實體化物件,在該物件中實作了onPictureTaken(byte[] data, Camera camera)方法,是攝像頭采集到拍攝的資料后回呼該方法,其中的 data 引數便是具體的照片資料,而 camera 則是對應的攝像頭物件;三個引數不同的是,引數 raw 是回傳的原始資料、引數 postview 是回傳的是縮略圖資料、引數 jpeg 則是回傳的經過jpeg編碼的壓縮資料,
視頻錄制
如果想實作實作攝像頭的錄制視頻功能,在呼叫Camera物件的startPreview()方法開啟預覽后,還要呼叫其unlock()方法將該攝像頭物件從當前行程解鎖,以便之后將該攝像頭物件配置到android.media.MediaRecorder多媒體錄制類中,在多媒體錄制類結束錄制并關閉釋放相關資源后,呼叫Camera物件的reconnect ()方法重新將該攝像頭與當前行程鎖定,這樣便可以在當前行程中繼續使用該攝像頭物件了,
關于使用MediaRecorder多媒體錄制類的相關流程,將在后續文章中詳細講解,
目標版本為API 21及以上
從Android5.0版本系統開始,可以在應用程式專案組態檔中增加androidx.camera:camera-core和androidx.camera:camera-camera2等官方提供的CameraX框架的依賴庫,該庫將攝像頭的功能分別作為單獨的類處理,而不是繼續使用低版本將功能都添加到同一個Camera類中,
首先是檢測設備是否支持攝像頭硬體,同樣是在能獲取Context背景關系環境物件的地方,借助androidx.camera.lifecycle.ProcessCameraProvider攝像頭提供者類的靜態方法getInstance(Context context)獲取提供者的行程間唯一的單例物件,回傳的是ListenableFuture<ProcessCameraProvider>型別的結果,這里的ListenableFuture是谷歌提供的 guava 框架下com.google.common.util包中的異步任務,簡單來說就是該型別的物件可以呼叫addListener(Runnable runnable, Executor executor)監聽方法,在該物件所系結的異步任務完成后會回呼監聽方法中的引數 runnable 運行,而引數 executor 則指定了運行 runnable 所在的執行緒,通過使用ContextCompat.getMainExecutor(Context context)方法獲取UI主執行緒的Executor物件,而這里通過攝像頭提供者類的靜態方法獲取的單例物件,就是對應的異步任務,在回傳ListenableFuture<ProcessCameraProvider>物件后,為該物件增加監聽方法,在異步任務完成后才會呼叫監聽方法中的內容,
在引數 runnable 定義的運行程序中,便可以直接使用之前的ListenableFuture<ProcessCameraProvider>物件的get()方法,回傳ProcessCameraProvider型別的單例物件以實作攝像頭功能,
同樣可以呼叫ProcessCameraProvider物件的getAvailableCameraInfos()方法獲取可以訪問的攝像頭詳細資訊,得到androidx.camera.core.CameraInfo攝像頭資訊物件組成的串列,
預覽
實作預覽功能,主要依靠androidx.camera.view.PreviewView預覽視圖類作為系統控制元件來實時展示攝像頭采集的資料,最終在代碼中呼叫PreviewView物件的getSurfaceProvider()方法,可以獲取androidx.camera.core.Preview.SurfaceProvider預覽提供者型別的物件,為之后將該控制元件與androidx.camera.core.Preview預覽類系結,
之后需要創建androidx.camera.core.Preview預覽類,其創建方式遵循建造者模式,構造androidx.camera.core.Preview.Builder建造者物件,使用該物件的setX系列方法可以配置預覽資訊,最終呼叫建造者物件的build()方法回傳創建Preview預覽類物件,
得到Preview物件后,呼叫setSurfaceProvider(Preview.SurfaceProvider surfaceProvider)方法系結預覽視圖控制元件,引數 surfaceProvider 即上文預覽視圖控制元件物件中的Preview.SurfaceProvider型別的預覽提供者物件,
最終,只需將該Preview物件系結到ProcessCameraProvider攝像頭提供者物件中,在上文獲取到攝像頭提供者的異步任務完成監聽中,呼叫ProcessCameraProvider物件的bindToLifecycle (LifecycleOwner lifecycleOwner, CameraSelector cameraSelector, UseCase... useCases)方法將攝像頭、預覽、分別與當前界面生命周期系結即可,其中引數 lifecycleOwner 為當前界面Activity 物件;
引數 cameraSelector 是通過建造者模式創建的androidx.camera.core.CameraSelector攝像頭選擇器物件,通過先構造androidx.camera.core.CameraSelector.Builder建造者物件,使用該物件的requireLensFacing(int lensFacing)方法來選擇要使用的攝像頭型別,其引數 lensFacing 值只能為前置攝像頭的CameraSelector.LENS_FACING_FRONT=0或后置攝像頭的CameraSelector.LENS_FACING_BACK=1,之后同樣呼叫build()方法回傳創建的CameraSelector物件;
可變引數 useCases 即包括上文中的Preview物件和下文的其他功能對應的案例物件,
拍照
實作拍照功能,主要依靠androidx.camera.core.ImageCapture圖片捕獲類,該類同樣使用建造者模式創建,首先構造androidx.camera.core.ImageCapture.Builder建造者物件,呼叫該物件的setX系列方法,可以設定拍照時的引數資訊,最終呼叫該物件的build()方法,回傳創建的圖片捕獲物件,
在得到ImageCapture圖片拍攝類物件后,同樣需要呼叫ProcessCameraProvider攝像頭提供者物件的bindToLifecycle (LifecycleOwner lifecycleOwner, CameraSelector cameraSelector, UseCase... useCases)方法將攝像頭與當前拍照物件系結,引數 lifecycleOwner 和 cameraSelector 與上文使用相同,而引數 useCases 則是這里的ImageCapture圖片拍攝類物件,
最終在需要拍照的時刻,呼叫ImageCapture圖片拍攝類物件的takePicture(ImageCapture.OutputFileOptions outputFileOptions, Executor executor, ImageCapture.OnImageSavedCallback imageSavedCallback)方法即可,其中,
引數 outputFileOptions 是用建造者模式的輸出檔案選項,同樣是通過構造androidx.camera.core.ImageCapture.OutputFileOptions.Builder建造者,設定要保存的檔案路徑,最終建造回傳androidx.camera.core.ImageCapture.OutputFileOptions型別物件使用即可;
引數 executor 是下一個引數 imageSavedCallback 回呼方法被運行時所在的執行緒;
引數 imageSavedCallback 是androidx.camera.core.ImageCapture.OnImageCapturedCallback照片捕獲后回呼介面的實體化物件,在該物件中需要實作onCaptureSuccess(ImageProxy image)在圖片拍攝成功時的回呼方法,和onError(ImageCaptureException exception)在圖片拍攝出錯時的回呼方法,
視頻錄制
實作視頻錄制功能,主要依靠androidx.camera.video.VideoCapture視頻捕獲類,
這里的VideoCapture視頻捕獲類可就不是建造者模式創建的了,而是使用其靜態方法withOutput(T videoOutput),傳入引數 videoOutput 為視頻輸出流,回傳VideoCapture的實體化物件,這里的視頻輸出流通常為androidx.camera.video.Recorder視頻錄制型別,Recorder視頻錄制類的物件是后面的操作物件,因此他才是用建造者模式創建的,
先構造androidx.camera.video.Recorder.Builder建造者,通過建造者物件的setQualitySelector(QualitySelector qualitySelector)方法為錄制的視頻流設定壓縮質量,該方法的引數 qualitySelector 通常是由androidx.camera.video.QualitySelector質量選擇器的靜態方法getSupportedQualities(CameraInfo cameraInfo)獲取支持的壓縮質量串列,其值包括最低解析度的QualitySelector.QUALITY_LOWEST=0、最高解析度的QualitySelector.QUALITY_HIGHEST=2、480P解析度的QualitySelector,QUALITY_SD=4、720P的QualitySelector.QUALITY_HD=5、1080P的QualitySelector.QUALITY_FHD=6、2160P的QualitySelector.QUALITY_UHD=8,在獲取質量串列的靜態方法中,引數 canmeraInfo 是錄制使用的攝像頭資訊物件,最終呼叫建造者的build()方法,回傳創建的Recorder錄制視頻流物件,
在得到VideoCapture視頻捕獲類物件后,同樣需要呼叫ProcessCameraProvider攝像頭提供者物件的bindToLifecycle (LifecycleOwner lifecycleOwner, CameraSelector cameraSelector, UseCase... useCases)方法將攝像頭與當前視頻捕獲物件系結,在引數 useCases 中增加VideoCapture物件即可,
在VideoCapture視頻捕獲類物件系結之后,通過呼叫其getOutput()方法回傳其設定的Recorder視頻錄制物件,
通過呼叫Recorder物件的prepareRecording(Context context, FileOutputOptions fileOutputOptions)方法準備錄制視頻,其引數 context 為背景關系環境物件,引數 fileOutputOptions 與拍攝照片時類似的使用androidx.camera.video.FileOutputOptions輸出檔案選項物件,用以設定錄制視頻的保存路徑,該方法回傳androidx.camera.video.PendingRecording預備錄制型別的物件,
在得到的PendingRecording物件中,可以呼叫start()方法,啟動視頻錄制,回傳androidx.camera.video.ActiveRecording活動錄制型別物件,
在得到的ActiveRecording物件中,可以呼叫pause()方法暫停錄制,resume()方法繼續錄制,stop()方法停止錄制,如此,便可完成視頻的錄制流程,
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/374509.html
標籤:其他
