公衆號回覆:666,領取學習資源大禮包
來源:svvvvvvvL
地址:https://www.jianshu.com/p/0d2f200ab374
Android的相機 Camera2 在 6.0M 的時候,出了一個支持高幀率預覽和錄像的功能。
就是創建一個新的 session
,叫做 mCameraDevice.createConstrainedHighSpeedCaptureSession
,通過這個,可以實現相機的高幀率(>120fps)的預覽和錄像(需要相機本身支持)。
根據相機的不同,實現的幀率也不同, 比如我手上這個華爲v10的手機就是下圖這個樣子:
下面說一下大概步驟.
第一步, 獲取權限
相機部分肯定得請求相關權限才能操作的,這裏需要3個,分別爲
相機 Manifest.permission.CAMERA
錄音 Manifest.permission.RECORD_AUDIO 這個是爲了錄視頻的時候錄音用的
寫入文件 Manifest.permission.WRITE_EXTERNAL_STORAGE 這個是拍好了視屏,存入到手機相冊用的
除了運行時請求這些權限之外, 還需要在AndroidManifest.xml文件裏面加入這3個權限的註冊,否則請求權限的時候,不能觸發Dialog提示
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
第二步,打開相機
正確獲取到權限了之後,按照常規方式打開相機.
先獲取到
CameraManager manager = (CameraManager) activity.getSystemService(Context.CAMERA_SERVICE);
然後用這個manager打開相機,即
manager.openCamera(cameraId, mStateCallback, mBackgroundHandler);
在這中間,我們可以獲取到相機的支持的各種參數信息, 也就是在這裏纔可以知道,當前的相機支不支持高幀率錄製
讀取到的支持預覽高幀率
使用 manager.getCameraCharacteristics(cameraId)
獲取 CameraCharacteristics
。
然後使用 characteristics .get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)
這個值可以獲取到向前攝像頭支持的參數 StreamConfigurationMap
。
最後用 map
調用 map.getHighSpeedVideoFpsRanges()
來獲取支持搞幀率預覽範圍,代碼部分爲
CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);
StreamConfigurationMap map = characteristics
.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
if (map == null) {
ErrorDialog.newInstance(getString(R.string.open_failed_of_map_null))
.show(getFragmentManager(), "TAG");
return;
}
for (Range<Integer> fpsRange : map.getHighSpeedVideoFpsRanges()) {
Log.d(TAG, "openCamera: [width, height] = "+ fpsRange.toString());
}
比如我手上的這個華爲V10測試機,獲取到的參數就是
openCamera: [width, height] = [120, 120]
openCamera: [width, height] = [30, 120]
幀率範圍Range的 Lower
和 Upper
一致才認爲支持,所以這個log結果說明它支持120fps
的預覽.
支持高幀率的視頻錄製的條件
高幀率的視頻錄製和普通的camera.createCaptureSession要求不同,高幀率session的預覽preview大小和設置的MediaRecorder的大小必須一致
上文獲取出來的支持120fps,說明他支持預覽,但是不意味着它支持錄製視頻。
是否支持視頻錄製,需要用另一個方法來查詢, 就是
CamcorderProfile
的public static boolean hasProfile(int cameraId, int quality)
方法,只有他返回true才意味着當前預覽的幀率支持被錄製視頻。
爲什麼一定要用CamcorderProfile來判斷是否支持?
直接在MediaRecorder的方法裏set設置各個屬性是否可行?
答案是不行, 雖然我也不知道爲什麼, 我用手邊的機器堅果Pro 2s測試, 查詢到支持的預覽幀率支持1080p的240fps,但是在給MediaRecorder設置好各種參數後,比如mMediaRecorder.setVideoFrameRate(240),點擊錄製直接crash.
配置MediaRecorder
在得出手機支持高幀率錄製後, 就需要配置MediaRecorder給下一步打開預覽使用了. 具體的步驟如下:
CamcorderProfile profile = mVideoSize.getCamcorderProfile();
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
mMediaRecorder.setProfile(profile);
mNextVideoFilePath = getVideoFile();
mMediaRecorder.setOutputFile(mNextVideoFilePath);
int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
int orientation = ORIENTATIONS.get(rotation);
mMediaRecorder.setOrientationHint(orientation);
mMediaRecorder.prepare();
這裏面的關鍵就是setVideoFrameRate這一個屬性, 這個只能由CamcorderProfile.videoFrameRate來設置, 如果CamcorderProfile說你不支持, 那麼你手動設置了這個數值, 也不能正常錄製.
其他的只需要將CamcorderProfile不包含的配置設置進去就好了. 其他的比如setOutputFile輸出文件路徑,setOrientationHint旋轉方向這些, 根據需要配置就好了.
第三步, 開啓預覽
使用CameraDevice的如下方法開啓預覽
/*
* @param outputs The new set of Surfaces that should be made available as
* targets for captured high speed image data.
* @param callback The callback to notify about the status of the new capture session.
* @param handler The handler on which the callback should be invoked, or {@code null} to use
* the current thread's {@link android.os.Looper looper}.
*
* @throws IllegalArgumentException if the set of output Surfaces do not meet the requirements,
* the callback is null, or the handler is null but the current
* thread has no looper, or the camera device doesn't support
* high speed video capability.
* @throws CameraAccessException if the camera device is no longer connected or has
* encountered a fatal error
* @throws IllegalStateException if the camera device has been closed
*
* @see #createCaptureSession
* @see CaptureRequest#CONTROL_AE_TARGET_FPS_RANGE
* @see StreamConfigurationMap#getHighSpeedVideoSizes
* @see StreamConfigurationMap#getHighSpeedVideoFpsRangesFor
* @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
* @see CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO
* @see CameraCaptureSession#captureBurst
* @see CameraCaptureSession#setRepeatingBurst
* @see CameraConstrainedHighSpeedCaptureSession#createHighSpeedRequestList
*/
public abstract void createConstrainedHighSpeedCaptureSession(@NonNull List<Surface> outputs,
@NonNull CameraCaptureSession.StateCallback callback,
@Nullable Handler handler)
throws CameraAccessException;
可以看到, 第一個參數是一個surface的List,代表着相機攝像頭拍得到畫面要輸出到哪裏去, 這裏我們有2個目的地:
第一個是preview,需要輸出到預覽, 這樣我們在屏幕上纔看得到畫面 第二個是mMediaRecorder.getSurface(), 這個是輸出到MediaRecorder裏面去,用來錄製視頻
所以這個地方的代碼就類似
Surface previewSurface = new Surface(texture);
//添加預覽輸出
surfaces.add(previewSurface);
mPreviewBuilder.addTarget(previewSurface);
//配置MediaRecorder
setUpMediaRecorder();
//添加MediaRecorder輸出
surfaces.add(mMediaRecorder.getSurface());
mPreviewBuilder.addTarget(mMediaRecorder.getSurface());
//開啓預覽
mCameraDevice.createConstrainedHighSpeedCaptureSession(surfaces, new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) {
//保存引用
mPreviewSessionHighSpeed = (CameraConstrainedHighSpeedCaptureSession) cameraCaptureSession;
//刷新預覽, 使屏幕動起來
updatePreview();
}
@Override
public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession) {
Activity activity = getActivity();
if (null != activity) {
Toast.makeText(activity, "Failed", Toast.LENGTH_SHORT).show();
}
}
}, mBackgroundHandler);
沒什麼錯誤的話,這樣就可以啓動預覽了.
第四步, 開始錄像和結束錄像
因爲MediaRecorder在之前已經準備就緒了, 所以開啓錄像只需要調用一下開始錄像就好了
mMediaRecorder.start();
其他的一些UI操作看着辦.
結束錄製也是幾句話:
//停止錄製
mMediaRecorder.stop();
//重置狀態,便於重用, 不需要每次都new MediaRecorder了
mMediaRecorder.reset();
//因爲MediaRecorder被銷燬了, 所以需要重新配置MediaRecorder,重新打開預覽
startPreview();
第五步, 驗證視頻的幀率
對於錄製生成的視頻, 需要查看他的具體參數是不是真的是我們所設置的值. 我這裏是macos環境, 就只演示我自己的操作:
用USB線連接手機和電腦,點擊右下角的 Device File Explorer
在文件列表中找到自己錄製的視頻文件, 並把它保存到本地,比如Downloads
保存到本地的視頻文件
打開終端Terminal,然後運行命令ffmpeg -i 視頻文件名, 如果提示ffmpeg命令不存在或者找不到,就先安裝這個東西
紅線處是查詢視頻信息的命令,綠線是該視頻的fps幀率信息. 雖然我們預覽的是120fps,但是錄製出來的不一定可以達到那麼高, 比如圖裏面的就只有74.54fps.這是系統自己決定的.
最後
關鍵就是2點:
預覽尺寸和錄製尺寸要一直
CamcorderProfile 說你支持你才支持.
所有Demo代碼提交到Github.可以訪問
https://github.com/ZhengShang/HighSpeedVideoDemo/tree/master
如果運行apk後發現crash,ANR或者黑屏等雜七雜八的問題, 這都很正常, Android相機開發受制於各種不同的手機和rom的差異化,確實挺難做到完美兼容的, 尤其是這隨便寫的Demo,就更難保證了.
但是大體流程是這個樣子, 具體解決Bug那都是後事了.
推薦閱讀:
覺得不錯,點個在看唄~