@Override
  public void onTextureFrameAvailable(int oesTextureId, float[] transformMatrix, long timestampNs) {
    checkIsOnCameraThread();
    if (camera == null) {
      // Camera is stopped, we need to return the buffer immediately.
      surfaceHelper.returnTextureFrame();
      return;
    }
    if (!dropNextFrame) {
      surfaceHelper.returnTextureFrame();
      dropNextFrame = true;
      return;
    }

    int rotation = getFrameOrientation();
    if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
      // Undo the mirror that the OS "helps" us with.
      // http://developer.android.com/reference/android/hardware/Camera.html#setDisplayOrientation(int)
      transformMatrix =
          RendererCommon.multiplyMatrices(transformMatrix, RendererCommon.horizontalFlipMatrix());
    }
    transformMatrix = RendererCommon.rotateTextureMatrix(transformMatrix, rotation);

    final int rotatedWidth = (rotation % 180 == 0) ? captureFormat.width : captureFormat.height;
    final int rotatedHeight = (rotation % 180 == 0) ? captureFormat.height : captureFormat.width;
    cameraStatistics.addPendingFrame(timestampNs);
    frameObserver.onTextureFrameCaptured(
        rotatedWidth, rotatedHeight, oesTextureId, transformMatrix, timestampNs);
  }
  // Called on cameraThread so must not "synchronized".
  @Override
  public void onPreviewFrame(byte[] data, Camera callbackCamera) {
    checkIsOnCameraThread();
    if (camera == null) {
      return;
    }
    if (camera != callbackCamera) {
      throw new RuntimeException("Unexpected camera in callback!");
    }

    final long captureTimeNs = TimeUnit.MILLISECONDS.toNanos(SystemClock.elapsedRealtime());

    if (eventsHandler != null && !firstFrameReported) {
      eventsHandler.onFirstFrameAvailable();
      firstFrameReported = true;
    }

    // Mark the frame owning |data| as used.
    // Note that since data is directBuffer,
    // data.length >= videoBuffers.frameSize.
    if (videoBuffers.reserveByteBuffer(data, captureTimeNs)) {
      cameraStatistics.addPendingFrame(captureTimeNs);
      frameObserver.onByteBufferFrameCaptured(
          data,
          videoBuffers.frameSize,
          captureFormat.width,
          captureFormat.height,
          getFrameOrientation(),
          captureTimeNs);
    } else {
      Logging.w(TAG, "reserveByteBuffer failed - dropping frame.");
    }
  }
 private void onOutputFormatRequestOnCameraThread(int width, int height, int framerate) {
   checkIsOnCameraThread();
   if (camera == null) {
     Logging.e(TAG, "Calling onOutputFormatRequest() on stopped camera.");
     return;
   }
   Logging.d(
       TAG, "onOutputFormatRequestOnCameraThread: " + width + "x" + height + "@" + framerate);
   frameObserver.onOutputFormatRequest(width, height, framerate);
 }
  private void startCaptureOnCameraThread(
      final int width,
      final int height,
      final int framerate,
      final CapturerObserver frameObserver,
      final Context applicationContext) {
    Throwable error = null;
    checkIsOnCameraThread();
    if (camera != null) {
      throw new RuntimeException("Camera has already been started.");
    }
    this.applicationContext = applicationContext;
    this.frameObserver = frameObserver;
    this.firstFrameReported = false;

    try {
      try {
        synchronized (cameraIdLock) {
          Logging.d(TAG, "Opening camera " + id);
          if (eventsHandler != null) {
            eventsHandler.onCameraOpening(id);
          }
          camera = Camera.open(id);
          info = new Camera.CameraInfo();
          Camera.getCameraInfo(id, info);
        }
      } catch (RuntimeException e) {
        openCameraAttempts++;
        if (openCameraAttempts < MAX_OPEN_CAMERA_ATTEMPTS) {
          Logging.e(TAG, "Camera.open failed, retrying", e);
          openCameraOnCodecThreadRunner =
              new Runnable() {
                @Override
                public void run() {
                  startCaptureOnCameraThread(
                      width, height, framerate, frameObserver, applicationContext);
                }
              };
          cameraThreadHandler.postDelayed(openCameraOnCodecThreadRunner, OPEN_CAMERA_DELAY_MS);
          return;
        }
        openCameraAttempts = 0;
        throw e;
      }

      try {
        camera.setPreviewTexture(surfaceHelper.getSurfaceTexture());
      } catch (IOException e) {
        Logging.e(TAG, "setPreviewTexture failed", error);
        throw new RuntimeException(e);
      }

      Logging.d(
          TAG,
          "Camera orientation: "
              + info.orientation
              + " .Device orientation: "
              + getDeviceOrientation());
      camera.setErrorCallback(cameraErrorCallback);
      startPreviewOnCameraThread(width, height, framerate);
      frameObserver.onCapturerStarted(true);

      // Start camera observer.
      cameraThreadHandler.postDelayed(cameraObserver, CAMERA_OBSERVER_PERIOD_MS);
      return;
    } catch (RuntimeException e) {
      error = e;
    }
    Logging.e(TAG, "startCapture failed", error);
    stopCaptureOnCameraThread();
    frameObserver.onCapturerStarted(false);
    if (eventsHandler != null) {
      eventsHandler.onCameraError("Camera can not be started.");
    }
    return;
  }