影片直播技術–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 步。