Как открыть как переднюю и заднюю камеру, в то же время на Samsung Galaxy Devices?

Обновить

April 2019

Просмотры

283 раз

1

This is a question that is still left unanswered as i have gone through all the questions in SO.

...and so on but the real problem is that i have a perfectly working dual camera tested on multiple devices like: Google Pixel XL, LETV 1S, Elephone S7, LG Devices, practically most devices that have two Image Signal Processors, except for Samsung Devices, which show only 1 camera preview when tested.

I have tried so far opening both cameras with OpenGL, Deprecated Camera API, Camera2 API, and lastly the very own Samsung Camera Api and still the same results.

Below is the Samsung API CameraPreview

public class SCamera {
private SCamera mSCamera;
  private SCameraManager mSCameraManager;
  private SCameraDevice mSCameraDevice;
  private SCameraCaptureSession mSCameraSession;
  private SCameraCharacteristics mCharacteristics;
  private SCaptureRequest.Builder mPreviewBuilder;
  /**
   * Current Preview Size.
   */
  private Size mPreviewSize;
  /**
   * Current Picture Size.
   */
  private Size mPictureSize;
  /**
   * ID of the current {@link com.samsung.android.sdk.camera.SCameraDevice}.
   */
  private String mCameraId;
  /**
   * for camera preview.
   */
  private TextureView mTextureView;
  /**
   * A camera related listener/callback will be posted in this handler.
   */
  private Handler mBackgroundHandler;
  private HandlerThread mBackgroundHandlerThread;
  /**
   * A image saving worker Runnable will be posted to this handler.
   */
  private HandlerThread mImageSavingHandlerThread;
  /**
   * An orientation listener for jpeg orientation
   */
  private int mLastOrientation = 0;
  private Semaphore mCameraOpenCloseLock = new Semaphore(1);
  /**
   * Lens facing. Camera with this facing will be opened
   */
  private int mLensFacing;
  private List<Integer> mLensFacingList;
  private Activity context;

  public SamsungApiManager(Activity context, TextureView textureView, int cameraId) {
    this.context = context;
    this.mTextureView = textureView;
    startBackgroundThread();

    // initialize SCamera
    mSCamera = new SCamera();
    try {
      mSCamera.initialize(context);
    } catch (SsdkUnsupportedException e) {
      e.printStackTrace();
      return;
    }
    createUI();
    checkRequiredFeatures(cameraId);
    openCamera(mLensFacing);
  }

  private void checkRequiredFeatures(int cameraId) {
    try {
      // Find available lens facing value for this device
      Set<Integer> lensFacings = new HashSet<>();
      for (String id : mSCamera.getSCameraManager().getCameraIdList()) {
        SCameraCharacteristics cameraCharacteristics =
            mSCamera.getSCameraManager().getCameraCharacteristics(id);
        lensFacings.add(cameraCharacteristics.get(SCameraCharacteristics.LENS_FACING));
      }
      mLensFacingList = new ArrayList<>(lensFacings);

      mLensFacing = mLensFacingList.get(cameraId);

      setDefaultJpegSize(mSCamera.getSCameraManager(), mLensFacing);
    } catch (CameraAccessException e) {
      e.printStackTrace();
      Log.e("Camera", "Cannot access the camera.", e);
    }
  }

  /**
   * Starts back ground thread that callback from camera will posted.
   */
  private void startBackgroundThread() {
    mBackgroundHandlerThread = new HandlerThread("Background Thread");
    mBackgroundHandlerThread.start();
    mBackgroundHandler = new Handler(mBackgroundHandlerThread.getLooper());

    mImageSavingHandlerThread = new HandlerThread("Saving Thread");
    mImageSavingHandlerThread.start();
  }

  /**
   * Starts a preview.
   */
  synchronized private void startPreview() {
    if (mSCameraSession == null) return;
    try {
      // Starts displaying the preview.
      mSCameraSession.setRepeatingRequest(mPreviewBuilder.build(), null, mBackgroundHandler);
    } catch (CameraAccessException e) {
      Log.e("Error", e.getMessage());
      e.printStackTrace();
    }
  }

  /**
   * Stops back ground thread.
   */
  private void stopBackgroundThread() {
    if (mBackgroundHandlerThread != null) {
      mBackgroundHandlerThread.quitSafely();
      try {
        mBackgroundHandlerThread.join();
        mBackgroundHandlerThread = null;
        mBackgroundHandler = null;
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }

    if (mImageSavingHandlerThread != null) {
      mImageSavingHandlerThread.quitSafely();
      try {
        mImageSavingHandlerThread.join();
        mImageSavingHandlerThread = null;
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
  }

  private void setDefaultJpegSize(SCameraManager manager, int facing) {
    try {
      for (String id : manager.getCameraIdList()) {
        SCameraCharacteristics cameraCharacteristics = manager.getCameraCharacteristics(id);
        if (cameraCharacteristics.get(SCameraCharacteristics.LENS_FACING) == facing) {
          List<Size> jpegSizeList = new ArrayList<>();

          if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
              && cameraCharacteristics.get(SCameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)
              .getHighResolutionOutputSizes(ImageFormat.JPEG) != null) {
            jpegSizeList.addAll(Arrays.asList(
                cameraCharacteristics.get(SCameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)
                    .getHighResolutionOutputSizes(ImageFormat.JPEG)));
          }
          jpegSizeList.addAll(Arrays.asList(
              cameraCharacteristics.get(SCameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)
                  .getOutputSizes(ImageFormat.JPEG)));
          mPictureSize = jpegSizeList.get(0);
        }
      }
    } catch (CameraAccessException e) {
      Log.e("Camera", "Cannot access the camera.", e);
    }
  }

  /**
   * Opens a {@link com.samsung.android.sdk.camera.SCameraDevice}.
   */
  synchronized public void openCamera(int facing) {
    try {
      if (!mCameraOpenCloseLock.tryAcquire(3000, TimeUnit.MILLISECONDS)) {
        Log.e("Error", "time out");
      }

      mSCameraManager = mSCamera.getSCameraManager();

      mCameraId = null;

      // Find camera device that facing to given facing parameter.
      for (String id : mSCamera.getSCameraManager().getCameraIdList()) {
        SCameraCharacteristics cameraCharacteristics =
            mSCamera.getSCameraManager().getCameraCharacteristics(id);
        if (cameraCharacteristics.get(SCameraCharacteristics.LENS_FACING) == facing) {
          mCameraId = id;
          break;
        }
      }

      if (mCameraId == null) {
        Log.e("Error", "no id found");
        return;
      }

      // acquires camera characteristics
      mCharacteristics = mSCamera.getSCameraManager().getCameraCharacteristics(mCameraId);

      StreamConfigurationMap streamConfigurationMap =
          mCharacteristics.get(SCameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);

      // Acquires supported preview size list that supports SurfaceTexture
      mPreviewSize =
          getOptimalPreviewSize(streamConfigurationMap.getOutputSizes(SurfaceTexture.class),
              (double) mPictureSize.getWidth() / mPictureSize.getHeight());

      Log.d("Camera",
          "Picture Size: " + mPictureSize.toString() + " Preview Size: " + mPreviewSize.toString());

      if (contains(mCharacteristics.get(SCameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES),
          SCameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) {
        List<Size> rawSizeList = new ArrayList<>();

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
            && streamConfigurationMap.getHighResolutionOutputSizes(ImageFormat.RAW_SENSOR)
            != null) {
          rawSizeList.addAll(Arrays.asList(
              streamConfigurationMap.getHighResolutionOutputSizes(ImageFormat.RAW_SENSOR)));
        }
        rawSizeList.addAll(
            Arrays.asList(streamConfigurationMap.getOutputSizes(ImageFormat.RAW_SENSOR)));
      }

      // Opening the camera device here
      mSCameraManager.openCamera(mCameraId, new SCameraDevice.StateCallback() {
        @Override
        public void onDisconnected(SCameraDevice sCameraDevice) {
          mCameraOpenCloseLock.release();
        }

        @Override
        public void onError(SCameraDevice sCameraDevice, int i) {
          mCameraOpenCloseLock.release();
        }

        public void onOpened(SCameraDevice sCameraDevice) {
          mCameraOpenCloseLock.release();
          mSCameraDevice = sCameraDevice;
          createPreviewSession();
        }
      }, mBackgroundHandler);
    } catch (CameraAccessException e) {
      e.printStackTrace();
      Log.e("Camera", "Cannot open the camera.", e);
    } catch (InterruptedException e) {
      throw new RuntimeException("Interrupted while trying to lock camera opening.", e);
    }
  }

  /**
   * Closes a camera and release resources.
   */
  synchronized private void closeCamera() {
    try {
      mCameraOpenCloseLock.acquire();

      if (mSCameraSession != null) {
        mSCameraSession.close();
        mSCameraSession = null;
      }

      if (mSCameraDevice != null) {
        mSCameraDevice.close();
        mSCameraDevice = null;
      }

      mSCameraManager = null;
    } catch (InterruptedException e) {
      Log.e("Camera", "Interrupted while trying to lock camera closing.", e);
    } finally {
      mCameraOpenCloseLock.release();
    }
  }

  /**
   * Configures requires transform {@link android.graphics.Matrix} to TextureView.
   */
  private void configureTransform(int viewWidth, int viewHeight) {
    if (null == mTextureView || null == mPreviewSize) {
      return;
    }

    int rotation = context.getWindowManager().getDefaultDisplay().getRotation();
    Matrix matrix = new Matrix();
    RectF viewRect = new RectF(0, 0, viewWidth, viewHeight);
    RectF bufferRect = new RectF(0, 0, mPreviewSize.getHeight(), mPreviewSize.getWidth());
    float centerX = viewRect.centerX();
    float centerY = viewRect.centerY();
    if (Surface.ROTATION_90 == rotation || Surface.ROTATION_270 == rotation) {
      bufferRect.offset(centerX - bufferRect.centerX(), centerY - bufferRect.centerY());
      matrix.setRectToRect(viewRect, bufferRect, Matrix.ScaleToFit.FILL);
      float scale = Math.max((float) viewHeight / mPreviewSize.getHeight(),
          (float) viewWidth / mPreviewSize.getWidth());
      matrix.postScale(scale, scale, centerX, centerY);
      matrix.postRotate(90 * (rotation - 2), centerX, centerY);
    } else {
      matrix.postRotate(90 * rotation, centerX, centerY);
    }

    mTextureView.setTransform(matrix);
    mTextureView.getSurfaceTexture()
        .setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
  }

  private boolean contains(final int[] array, final int key) {
    for (final int i : array) {
      if (i == key) {
        return true;
      }
    }
    return false;
  }

  /**
   * Create a {@link com.samsung.android.sdk.camera.SCameraCaptureSession} for preview.
   */
  synchronized private void createPreviewSession() {

    if (null == mSCamera
        || null == mSCameraDevice
        || null == mSCameraManager
        || null == mPreviewSize
        || !mTextureView.isAvailable()) {
      return;
    }

    try {
      SurfaceTexture texture = mTextureView.getSurfaceTexture();

      // Set default buffer size to camera preview size.
      texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());

      Surface surface = new Surface(texture);

      // Creates SCaptureRequest.Builder for preview with output target.
      mPreviewBuilder = mSCameraDevice.createCaptureRequest(SCameraDevice.TEMPLATE_PREVIEW);
      mPreviewBuilder.addTarget(surface);

      // Creates SCaptureRequest.Builder for still capture with output target.
      //mCaptureBuilder = mSCameraDevice.createCaptureRequest(SCameraDevice.TEMPLATE_STILL_CAPTURE);

      // Creates a SCameraCaptureSession here.
      List<Surface> outputSurface = new ArrayList<Surface>();
      outputSurface.add(surface);
      //outputSurface.add(mJpegReader.getSurface());

      mSCameraDevice.createCaptureSession(outputSurface, new SCameraCaptureSession.StateCallback() {
        @Override
        public void onConfigureFailed(SCameraCaptureSession sCameraCaptureSession) {

        }

        @Override
        public void onConfigured(SCameraCaptureSession sCameraCaptureSession) {
          mSCameraSession = sCameraCaptureSession;
          startPreview();
        }
      }, mBackgroundHandler);
    } catch (CameraAccessException e) {
      e.printStackTrace();
    }
  }

  /**
   * Prepares an UI, like button, dialog, etc.
   */
  private void createUI() {
    // Set SurfaceTextureListener that handle life cycle of TextureView
    mTextureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
      @Override
      public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
        // "onSurfaceTextureAvailable" is called, which means that SCameraCaptureSession is not created.
        // We need to configure transform for TextureView and crate SCameraCaptureSession.
        configureTransform(width, height);
        createPreviewSession();
      }

      @Override
      public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
        return true;
      }

      @Override
      public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
        // SurfaceTexture size changed, we need to configure transform for TextureView, again.
        configureTransform(width, height);
      }

      @Override
      public void onSurfaceTextureUpdated(SurfaceTexture surface) {
      }
    });
  }

  /**
   * Returns required orientation that the jpeg picture needs to be rotated to be displayed upright.
   */
  private int getJpegOrientation() {
    int degrees = mLastOrientation;

    if (mCharacteristics.get(SCameraCharacteristics.LENS_FACING)
        == SCameraCharacteristics.LENS_FACING_FRONT) {
      degrees = -degrees;
    }

    return (mCharacteristics.get(SCameraCharacteristics.SENSOR_ORIENTATION) + degrees + 360) % 360;
  }

  /**
   * find optimal preview size for given targetRatio
   */
  private Size getOptimalPreviewSize(Size[] sizes, double targetRatio) {
    final double ASPECT_TOLERANCE = 0.001;

    Size optimalSize = null;
    double minDiff = Double.MAX_VALUE;

    Display display = context.getWindowManager().getDefaultDisplay();
    Point displaySize = new Point();
    display.getSize(displaySize);
    int targetHeight = Math.min(displaySize.y, displaySize.x);

    // Try to find an size match aspect ratio and size
    for (Size size : sizes) {
      double ratio = (double) size.getWidth() / size.getHeight();
      if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue;
      if (Math.abs(size.getHeight() - targetHeight) < minDiff) {
        optimalSize = size;
        minDiff = Math.abs(size.getHeight() - targetHeight);
      }
    }

    // Cannot find the one match the aspect ratio. This should not happen.
    // Ignore the requirement.
    if (optimalSize == null) {
      Log.w("Camera", "No preview size match the aspect ratio");
      minDiff = Double.MAX_VALUE;
      for (Size size : sizes) {
        if (Math.abs(size.getHeight() - targetHeight) < minDiff) {
          optimalSize = size;
          minDiff = Math.abs(size.getHeight() - targetHeight);
        }
      }
    }

    return optimalSize;
  }
}

Below is how i access both cameras simultaneously:

  private void openFrontCam() {

        textureFront.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
          @Override
          public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int i, int i1) {
            sCamera =
                new SCamera(DualCamera.this, textureFront, 0);
          }

          @Override
          public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int i, int i1) {

          }

          @Override
          public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) {
            return false;
          }

          @Override
          public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) {

          }
        });
      }



   private void openBackCam() {

        textureBack.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
          @Override
          public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int i, int i1) {
            sCamera =
                new SCamera(DualCamera.this, textureBack, 1);
          }

          @Override
          public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int i, int i1) {

          }

          @Override
          public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) {
            return false;
          }

          @Override
          public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) {

          }
        });
      }

Теперь я понимаю, что некоторые утверждают , что они получили двойную функцию камеры , работающую на Samsung устройствах , как в этом развернутом ответе , если Samsung не заперт функция только для их собственного использования.

Я исчерпал все варианты на сегодняшний день и не знаю, что там осталось, чтобы попробовать.

Благодарю.

2 ответы

0

I had similar problems to access the camera sensor (in my case I want to have maximum exposure time of 10 sec in my app). This is only provided by SemCam, the Samsung specific library which is secret and deeply hidden in their Android. According to my tests SemCam works until S8. With S9 and Note 9 Samsung switched completely to Camera2API. But they are Samsung: they've extended lots of Java classes of Camera2API which are only present on Samsung S9 and Note 9 devices, e.g. the Java class CameraDevice has new methods (setParameters()). I can't understand why Samsung is doing that. Their camera app is not the best and they are not the leader anymore. When you take a look at Huawei P devices or LG G/V and OnePLus or Xiaomi mi 8 /Pocophone they are working with Camera2API ONLY - without any extensions.

1

I managed to unlock this feature only on Samsung galaxy S8, S8+ and Note 8 devices, using their internal library SemCamera.jar and OpenGL with glsl shaders. However it is not compatible with older Samsung devices nor with the newest S9 devices. After reverse engineering their stock camera app to see if the feature exists, i saw that it was erased completely from their code. So their framework blocks 3rd party apps from using this feature and clearly not for security reasons. We hope that in the near future they will share the dual camera feature in the next Camera SDK.

Связанные вопросы