@Override
        public void run() {
          int cameraFramesCount = cameraStatistics.getAndResetFrameCount();
          int cameraFps =
              (cameraFramesCount * 1000 + CAMERA_OBSERVER_PERIOD_MS / 2)
                  / CAMERA_OBSERVER_PERIOD_MS;

          Logging.d(
              TAG,
              "Camera fps: "
                  + cameraFps
                  + ". Pending buffers: "
                  + cameraStatistics.pendingFramesTimeStamps());
          if (cameraFramesCount == 0) {
            ++freezePeriodCount;
            if (CAMERA_OBSERVER_PERIOD_MS * freezePeriodCount > CAMERA_FREEZE_REPORT_TIMOUT_MS
                && eventsHandler != null) {
              Logging.e(TAG, "Camera freezed.");
              if (cameraStatistics.pendingFramesCount() == cameraStatistics.maxPendingFrames) {
                eventsHandler.onCameraError("Camera failure. Client must return video buffers.");
              } else {
                eventsHandler.onCameraError("Camera failure.");
              }
              return;
            }
          } else {
            freezePeriodCount = 0;
          }
          cameraThreadHandler.postDelayed(this, CAMERA_OBSERVER_PERIOD_MS);
        }
  // 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 stopCaptureOnCameraThread() {
    checkIsOnCameraThread();
    Logging.d(TAG, "stopCaptureOnCameraThread");
    if (openCameraOnCodecThreadRunner != null) {
      cameraThreadHandler.removeCallbacks(openCameraOnCodecThreadRunner);
    }
    openCameraAttempts = 0;
    if (camera == null) {
      Logging.e(TAG, "Calling stopCapture() for already stopped camera.");
      return;
    }

    cameraThreadHandler.removeCallbacks(cameraObserver);
    cameraStatistics.getAndResetFrameCount();
    Logging.d(TAG, "Stop preview.");
    camera.stopPreview();
    camera.setPreviewCallbackWithBuffer(null);
    if (!isCapturingToTexture()) {
      videoBuffers.stopReturnBuffersToCamera();
      Logging.d(
          TAG,
          "stopReturnBuffersToCamera called."
              + (cameraStatistics.pendingFramesCount() == 0
                  ? " All buffers have been returned."
                  : " Pending buffers: " + cameraStatistics.pendingFramesTimeStamps() + "."));
    }
    captureFormat = null;

    Logging.d(TAG, "Release camera.");
    camera.release();
    camera = null;
    if (eventsHandler != null) {
      eventsHandler.onCameraClosed();
    }
  }
 @Override
 public void onError(int error, Camera camera) {
   String errorMessage;
   if (error == android.hardware.Camera.CAMERA_ERROR_SERVER_DIED) {
     errorMessage = "Camera server died!";
   } else {
     errorMessage = "Camera error: " + error;
   }
   Logging.e(TAG, errorMessage);
   if (eventsHandler != null) {
     eventsHandler.onCameraError(errorMessage);
   }
 }
  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;
  }