視頻直播技術–Android視頻採集(Camera1)
- 2020 年 4 月 2 日
- 筆記

camera1.jpg
前言
今天為大家介紹一下使用Camera1進行視頻採集。之前我寫過一篇文章介紹的是Camera2進行視頻採集。那麼有人會問,為什麼有了Camera2還要介紹Camera1呢?這裡最主要的原因是因為Android版本眾多,Camera2是Google新推出的視頻採集架構,但很多老的機型還不支持,所以為了兼容性的問題,我們還不能放棄使用Camera1進行視頻的採集。
下面我們來詳細介紹一下 Camera1 的使用步驟。
Camera1 使用步驟

camera1使用步驟.jpeg
如圖所示,使用 Camera1 的步驟包括下面幾大步:
- 設置Camera權限
- 檢查Camera是否可用
- 打開攝像頭
- 設置攝像頭參數
- 設置預覽
- 採集數據過程
詳細介紹
1. 申請Camera權限
第一步,在 AndroidManifast.xml中添加下面設置權限的語句。
<uses-permission android:name="android.permission.CAMERA" />
第二步,動態申請Camera權限。
Android在Android 6.0後,對根限的管理更嚴格了,除了上面要靜態申請權限外,還要通過調用 requestPermissions 函數動態申請Camera權限。requestPermissions函數如下:
void requestPermissions(String[] permissions, int requestCode);
2. 檢查Camera是否可用
為了程序的建壯性,在使用Camera之前我們最好檢測一下設備是否可用。檢測代碼中下:
public static void checkCameraService(Context context) throws CameraDisabledException, NoCameraException { //Check if device policy has disabled the camera. DevicePolicyManager dpm = (DevicePolicyManager)context.getSystemService(Context.DEVICE_POLICY_SERVICE); if (dpm.getCameraDisabled(null)) { throw new CameraDisabledException(); } ...... }
如果Camera服務可用,則還要檢查一下Camera的個數是否為 0 ? 如果為0 ,說明Camera也是不可用的。代碼如下:
...... Camera.CameraInfo cameraInfo = new Camera.CameraInfo(); int numberOfCameras = Camera.getNumberOfCameras(); ......
3. 打開攝像頭
private Camera mCamera; ...... Camera.CameraInfo info = new Camera.CameraInfo(); // Try to find a front-facing camera (e.g. for videoconferencing). int numCameras = Camera.getNumberOfCameras(); for (int i = 0; i < numCameras; i++) { Camera.getCameraInfo(i, info); if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) { mCamera = Camera.open(i); break; } } if (mCamera == null) { Log.d(TAG, "No front-facing camera found; opening default"); mCamera = Camera.open(); // opens first back-facing camera } if (mCamera == null) { throw new RuntimeException("Unable to open camera"); } ......
我們在打開Camera判斷是否打開成功,是通過Camera對象是否為null來判斷的。因為通過捕獲異常有時候是不準確的。
4. 設置攝像頭參數
設置攝像頭參數主要是設置圖像的寬、高、幀率。設置的基本步驟為:1. 從攝像頭取出現有參數。2. 修改參數。3. 設置參數。
...... Camera.Parameters parms = mCamera.getParameters(); CameraUtils.choosePreviewSize(parms, desiredWidth, desiredHeight); // Try to set the frame rate to a constant value. CameraUtils.chooseFixedPreviewFps(parms, desiredFps * 1000); // Give the camera a hint that we're recording video. This can have a big // impact on frame rate. parms.setRecordingHint(true); mCamera.setParameters(parms); ......
5. 設置預覽
開啟預覽的步驟如下:1. 通過 OpenGL ES生成外部紋理。 2. 通過紋理ID行成SurfaceTexture。 3. 將生成的紋理設置到Camera中。 4. 開啟預覽。5. 當有視頻幀到達後,使用OpengGL ES繪製圖片。
...... int[] textures = new int[1]; GLES20.glGenTextures(1, textures, 0); GlUtil.checkGlError("glGenTextures"); int texId = textures[0]; GLES20.glBindTexture(mTextureTarget, texId); ...... mCameraTexture = new SurfaceTexture(textureId); ...... // Ready to go, start the camera. Log.d(TAG, "starting camera preview"); try { mCamera.setPreviewTexture(mCameraTexture); } catch (IOException ioe) { throw new RuntimeException(ioe); } mCamera.startPreview(); ......
繪製圖片
private void frameAvailable() { mCameraTexture.updateTexImage(); draw(); }
通過上面的步驟,就將 OpenGL ES 、EGL、NativeWindow以及Camera之間建立起了聯接。
6.採集數據過程
- 打開Camera後,Camera開始採集數據。
- Camera會將數據存放到 mCameraTexture 中,也就是SurfaceTexture中。
- Camera完成一幀數據的採集後,通知應用程序有一幀數據已經準備好了。
- 應用程序收到通知後,調用mCameraTexture.updateTexImage(); 將SurfaceTexture中的數據輸出到外部紋理(也就是GLES20.glGenTextures函數產生的紋理中)。
- 通過 OpenGL ES 程序將外部紋理渲染到 EGL 的 EGLSurface中。
- 並最終調用 EGL14.eglSwapBuffers(mEGLDisplay, eglSurface); 將 EGLSurface中的內容輸出到 NativeWindow,最終顯示出來。
- 循環執行 1-6 步。