音視頻學(xué)習(xí)demo,正在升級打怪,加油鴨~
Android 音視頻開發(fā)(一) -- 使用AudioRecord 錄制PCM(錄音)荠瘪;AudioTrack播放音頻
Android 音視頻開發(fā)(二) -- Camera1 實現(xiàn)預(yù)覽夯巷、拍照功能
Android 音視頻開發(fā)(三) -- Camera2 實現(xiàn)預(yù)覽、拍照功能
工程:https://github.com/LillteZheng/VideoDemo
一. Camerax 簡介
在前面兩章中哀墓,我們已經(jīng)學(xué)習(xí)了 Camera1 Android 音視頻開發(fā)(二) -- Camera1 實現(xiàn)預(yù)覽趁餐、拍照功能 和 Camera2 Android 音視頻開發(fā)(三) -- Camera2 實現(xiàn)預(yù)覽、拍照功能篮绰,但從上面兩章可以看出來后雷,API 的調(diào)用還是比較繁瑣的,而且像一些最佳尺寸吠各,角度等還得自己算臀突,從 Camera2 的開發(fā)流程就可以知道:
真的很難記住這些流程;google 也意識到這個問題贾漏,所以推出了 Camerax 候学,它雖然底層也是利用了 Camera2 的功能,但它的使用卻更加簡潔纵散,它的主要優(yōu)勢如下:
- 更加簡潔的 API 調(diào)用盒齿,基本十幾行代碼就能實現(xiàn)預(yù)覽
- Camerax 為 Jetpack 的支持庫,所以也具備感知生命周期的功能困食,即你無需自己釋放 生命周期
- 更好兼容性
- 更多功能边翁,比如圖片分析等
注意,Camerax 也只支持 API21 及以上硕盹,本次效果如下:
二. Camerax 使用說明
Camerax 的使用非常簡單符匾,首先,添加關(guān)聯(lián)庫:
def camerax_version = "1.0.0-beta06"
// CameraX 核心庫
implementation "androidx.camera:camera-camera2:$camerax_version"
// CameraX 生命周期
implementation "androidx.camera:camera-lifecycle:$camerax_version"
// CameraX view 集合瘩例,比如 cameraview啊胶,preview等
implementation "androidx.camera:camera-view:1.0.0-alpha10"
權(quán)限
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera.any" />
2.1 預(yù)覽
首先,先通過
ListenableFuture<ProcessCameraProvider> cameraProviderFuture = ProcessCameraProvider.getInstance(this);
返回當(dāng)前可以綁定生命周期的 ProcessCameraProvider垛贤,ProcessCameraProvider 它會和宿主綁定生命周期焰坪,這樣就不用擔(dān)心打開相機(jī)和關(guān)閉的問題了。
接著聘惦,向 cameraProviderFuture 注冊一個監(jiān)聽某饰,第一個參數(shù)是一個 runnable,第二個參數(shù)是線程池,即runnable 運(yùn)行在哪個線程中:
cameraProviderFuture.addListener(Runnable {}, ContextCompat.getMainExecutor(this))
上面我們讓它運(yùn)行在主線程的線程池中黔漂,這樣诫尽,在這個 runnable 中,就可以配置我們的 capture 了:
//創(chuàng)建相機(jī)進(jìn)程監(jiān)聽
ListenableFuture<ProcessCameraProvider> cameraProviderFuture = ProcessCameraProvider.getInstance(this);
cameraProviderFuture.addListener(new Runnable() {
@SuppressLint("RestrictedApi")
@Override
public void run() {
try {
//將相機(jī)的生命周期和activity的生命周期綁定炬守,camerax 會自己釋放牧嫉,不用擔(dān)心了
ProcessCameraProvider cameraProvider = cameraProviderFuture.get();
//預(yù)覽的 capture,它里面支持角度換算
Preview preview = new Preview.Builder().build();
//創(chuàng)建圖片的 capture
mImageCapture = new ImageCapture.Builder()
.setFlashMode(ImageCapture.FLASH_MODE_AUTO)
.build();
//選擇后置攝像頭
CameraSelector cameraSelector = new CameraSelector.Builder().requireLensFacing(CameraSelector.LENS_FACING_BACK).build();
//預(yù)覽之前先解綁
cameraProvider.unbindAll();
//將數(shù)據(jù)綁定到相機(jī)的生命周期中
Camera camera = cameraProvider.bindToLifecycle(CameraxActivity.this, cameraSelector, preview, mImageCapture);
//將previewview 的 surface 給相機(jī)預(yù)覽
preview.setSurfaceProvider(mViewFinder.createSurfaceProvider(camera.getCameraInfo()));
} catch (Exception e) {
e.printStackTrace();
}
}
},ContextCompat.getMainExecutor(this));
可以看到减途,使用 ProcessCameraProvider.getInstance(this) 獲取當(dāng)前的 ProcessCameraProvider 去監(jiān)聽宿主的生命周期酣藻。
創(chuàng)建并確認(rèn) CameraProvider 后,執(zhí)行步驟如下
- 創(chuàng)建 Preview鳍置,使用 new Preview.Builder().build() 創(chuàng)建Preview臊恋,它是相機(jī)預(yù)覽的數(shù)據(jù)流,圖像數(shù)據(jù)會通過它來輸出墓捻,比如輸出到 Surface 中
- 創(chuàng)建 ImageCapture 用于拍照抖仅,這里的 builder 都可以設(shè)置參數(shù),比如大小砖第,曝光等
- 指定所需的相機(jī) LensFacing 選項撤卢,這里直接使用后置即可
- 將所選相機(jī)和任意用例綁定到生命周期,通過 cameraProvider.bindToLifecycle梧兼,將宿主的生命周期放吩,與 cameraSelector,preview和mImageCapture判定起來羽杰。
- 將 Preview 連接到 PreviewView渡紫,preview.setSurfaceProvider() 設(shè)置要預(yù)覽的 surface
上面的的 mViewFinder 其實是一個 PreviewView ,這是一種可以剪裁考赛、縮放和旋轉(zhuǎn)以確保正確顯示的 View惕澎,即不用你自己去設(shè)置預(yù)覽的角度等問題了。
<androidx.camera.view.PreviewView
android:id="@+id/viewFinder"
android:layout_width="match_parent"
android:layout_height="match_parent" />
2.2. 拍照
上面已經(jīng)設(shè)置了 mImageCapture颜骤,拍照呢唧喉,在 Camerax 中也很簡單,使用 takePicture 即可忍抽,如下
public void takePhoto(View view) {
if (mImageCapture != null) {
File dir = new File(Constants.PATH);
if (!dir.exists()) {
dir.mkdirs();
}
//創(chuàng)建文件
File file = new File(Constants.PATH,"testx.jpg");
if (file.exists()) {
file.delete();
}
//創(chuàng)建包文件的數(shù)據(jù)八孝,比如創(chuàng)建文件
ImageCapture.OutputFileOptions outputFileOptions = new ImageCapture.OutputFileOptions.Builder(file).build();
//開始拍照
mImageCapture.takePicture(outputFileOptions, ContextCompat.getMainExecutor(this), new ImageCapture.OnImageSavedCallback() {
@Override
public void onImageSaved(@NonNull ImageCapture.OutputFileResults outputFileResults) {
// Uri savedUri = outputFileResults.getSavedUri();
Toast.makeText(CameraxActivity.this, "保存成功: ", Toast.LENGTH_SHORT).show();
}
@Override
public void onError(@NonNull ImageCaptureException exception) {
Toast.makeText(CameraxActivity.this, "保存失敗", Toast.LENGTH_SHORT).show();
}
});
}
}
注釋已經(jīng)很清楚了,注意我們可以使用 ImageCapture.OutputFileOptions 讓 Camerax 去幫我們保存圖片文件鸠项。
參考:
https://codelabs.developers.google.com/codelabs/camerax-getting-started/#0