影片直播技術–Android影片採集(Camera1)

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.採集數據過程

  1. 打開Camera後,Camera開始採集數據。
  2. Camera會將數據存放到 mCameraTexture 中,也就是SurfaceTexture中。
  3. Camera完成一幀數據的採集後,通知應用程式有一幀數據已經準備好了。
  4. 應用程式收到通知後,調用mCameraTexture.updateTexImage(); 將SurfaceTexture中的數據輸出到外部紋理(也就是GLES20.glGenTextures函數產生的紋理中)。
  5. 通過 OpenGL ES 程式將外部紋理渲染到 EGL 的 EGLSurface中。
  6. 並最終調用 EGL14.eglSwapBuffers(mEGLDisplay, eglSurface); 將 EGLSurface中的內容輸出到 NativeWindow,最終顯示出來。
  7. 循環執行 1-6 步。

參考

  1. 影片渲染之EGL
  2. Android影片採集Camera2
  3. http://www.jianshu.com/p/39a015f2996e