Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to record video from a front camera without a mirror❓ #2338

Closed
3 of 4 tasks
HorbachAndrii opened this issue Jan 2, 2024 · 10 comments · Fixed by #3036
Closed
3 of 4 tasks

How to record video from a front camera without a mirror❓ #2338

HorbachAndrii opened this issue Jan 2, 2024 · 10 comments · Fixed by #3036
Labels
💭 question Further information is requested

Comments

@HorbachAndrii
Copy link

HorbachAndrii commented Jan 2, 2024

Question

I am analyzing recorded video and for me, it matters to record video without a mirror.

For IOS, I added some hack. I changed this connection.isVideoMirrored = false (from true to false) at node_modules/react-native-vision-camera/ios/Extensions/AVCaptureOutput+mirror.swift.

But for Android, I don't know how to record video from a front camera without a mirror.

Maybe somebody knows how can I record video from a front camera without a mirror?

What I tried

I changing the Matrix for transformMatrix in VideoPipeline.kt and other stuff, but nothing helped

VisionCamera Version

3.6.16

Additional information

@HorbachAndrii HorbachAndrii added the 💭 question Further information is requested label Jan 2, 2024
@HorbachAndrii
Copy link
Author

HorbachAndrii commented Jan 4, 2024

So I added also some hack that works for android.

VideoPipeline.kt node_modules/react-native-vision-camera/android/src/main/java/com/mrousavy/camera/core/VideoPipeline.kt

onFrame(transformMatrix)

->

onFrame(transformMatrix, isMirrored)
private external fun onFrame(transformMatrix: FloatArray)

->

private external fun onFrame(transformMatrix: FloatArray, isMirrored: Boolean)

VideoPipeline.cpp node_modules/react-native-vision-camera/android/src/main/cpp/VideoPipeline.cpp

void VideoPipeline::onFrame(jni::alias_ref<jni::JArrayFloat> transformMatrixParam) {

->

void VideoPipeline::onFrame(jni::alias_ref<jni::JArrayFloat> transformMatrixParam, bool isMirrored) {
_recordingSessionOutput->renderTextureToSurface(texture, transformMatrix);

->

_recordingSessionOutput->renderTextureToSurface(texture, transformMatrix, isMirrored);

VideoPipeline.h node_modules/react-native-vision-camera/android/src/main/cpp/VideoPipeline.h

void onFrame(jni::alias_ref<jni::JArrayFloat> transformMatrix);

->

void onFrame(jni::alias_ref<jni::JArrayFloat> transformMatrix, bool isMirrored);

OpenGLRenderer.cpp node_modules/react-native-vision-camera/android/src/main/cpp/OpenGLRenderer.cpp

void OpenGLRenderer::renderTextureToSurface(const OpenGLTexture& texture, float* transformMatrix) {

->

void OpenGLRenderer::renderTextureToSurface(const OpenGLTexture& texture, float* transformMatrix, bool isMirrored) {
_passThroughShader.draw(texture, transformMatrix);

->

_passThroughShader.draw(texture, transformMatrix, isMirrored);

OpenGLRenderer.h node_modules/react-native-vision-camera/android/src/main/cpp/OpenGLRenderer.h

void renderTextureToSurface(const OpenGLTexture& texture, float* transformMatrix);

->

void renderTextureToSurface(const OpenGLTexture& texture, float* transformMatrix, bool isMirrored);

PassThroughShader.cpp node_modules/react-native-vision-camera/android/src/main/cpp/PassThroughShader.cpp

 void PassThroughShader::draw(const OpenGLTexture& texture, float* transformMatrix) {

->

void PassThroughShader::draw(const OpenGLTexture& texture, float* transformMatrix, bool isMirrored) {
_programId = createProgram();

->

_programId = createProgram(isMirrored);
GLuint PassThroughShader::createProgram() {

->

GLuint PassThroughShader::createProgram(bool isMirrored) {
 GLuint fragmentShader = loadShader(GL_FRAGMENT_SHADER, FRAGMENT_SHADER);

->

GLuint fragmentShader;
if (isMirrored) {
  fragmentShader = loadShader(GL_FRAGMENT_SHADER, FRAGMENT_SHADER_FLIPPED);
} else {
  fragmentShader = loadShader(GL_FRAGMENT_SHADER, FRAGMENT_SHADER);
}

PassThroughShader.h node_modules/react-native-vision-camera/android/src/main/cpp/PassThroughShader.h

void draw(const OpenGLTexture& texture, float* transformMatrix);

->

void draw(const OpenGLTexture& texture, float* transformMatrix, bool isisMirrored);
static GLuint createProgram();

->

static GLuint createProgram(bool isisMirrored);
static constexpr char FRAGMENT_SHADER_FLIPPED[] = R"(
    #extension GL_OES_EGL_image_external : require
    precision mediump float;
    varying vec2 vTexCoord;
    uniform samplerExternalOES uTexture;

    void main() {
        gl_FragColor = texture2D(uTexture, vec2(vTexCoord.x, 1.0 - vTexCoord.y));
    }
  )";

@CyxouD
Copy link

CyxouD commented Apr 30, 2024

It doesn't work in my case on iOS with the same react-native-vision-camera. Could you say what iOS device you use? Also, could you send your Camera component JSX (if something differs here)?

@HorbachAndrii
Copy link
Author

HorbachAndrii commented Apr 30, 2024

It doesn't work in my case on iOS with the same react-native-vision-camera. Could you say what iOS device you use? Also, could you send your Camera component JSX (if something differs here)?

What VisionCamera Version are you using?

Could you say what iOS device you use?

it works for all IOS devices

@CyxouD
Copy link

CyxouD commented Apr 30, 2024

@HorbachAndrii thank you for your quick response!

What VisionCamera Version are you using?

I tried with 3.6.16 (as you mentioned) and 3.9.2. I tested with iPhone 11.

@HorbachAndrii
Copy link
Author

here is my AVCaptureOutput+mirror.swift file

//
//  AVCaptureOutput+mirror.swift
//  mrousavy
//
//  Created by Marc Rousavy on 18.01.21.
//  Copyright © 2021 mrousavy. All rights reserved.
//

import AVFoundation

extension AVCaptureOutput {
  /**
   Mirrors the video output if possible.
   */
  func mirror() {
    connections.forEach { connection in
      if connection.isVideoMirroringSupported {
        connection.automaticallyAdjustsVideoMirroring = false
        connection.isVideoMirrored = false
      }
    }
  }

  /**
   Sets the target orientation of the video output.
   This does not always physically rotate image buffers.

   - For Preview, an orientation hint is used to rotate the layer/view itself.
   - For Photos, an EXIF tag is used.
   - For Videos, the buffers are physically rotated if available, since we use an AVCaptureVideoDataOutput instead of an AVCaptureMovieFileOutput.
   */
  func setOrientation(_ orientation: Orientation) {
    // Set orientation for each connection
    connections.forEach { connection in
      #if swift(>=5.9)
        if #available(iOS 17.0, *) {
          // Camera Sensors are always in landscape rotation (90deg).
          // We are setting the target rotation here, so we need to rotate by landscape once.
          let cameraOrientation = orientation.rotateBy(orientation: .landscapeLeft)
          let degrees = cameraOrientation.toDegrees()

          // TODO: Don't rotate the video output because it adds overhead. Instead just use EXIF flags for the .mp4 file if recording.
          //       Does that work when we flip the camera?
          if connection.isVideoRotationAngleSupported(degrees) {
            connection.videoRotationAngle = degrees
          }
        } else {
          if connection.isVideoOrientationSupported {
            connection.videoOrientation = orientation.toAVCaptureVideoOrientation()
          }
        }
      #else
        if connection.isVideoOrientationSupported {
          connection.videoOrientation = orientation.toAVCaptureVideoOrientation()
        }
      #endif
    }
  }
}

@HorbachAndrii
Copy link
Author

@HorbachAndrii thank you for your quick response!

What VisionCamera Version are you using?

I tried with 3.6.16 (as you mentioned) and 3.9.2. I tested with iPhone 11.

Did you 'Clean Build Folder' before building the app?

@CyxouD
Copy link

CyxouD commented May 1, 2024

Did you 'Clean Build Folder' before building the app?
@HorbachAndrii Thank you! I tried it with 3.9.1 and 3.9.2, but unfortunately it didn't work. I didn't clean the build folder with 3.6.16 because it had another bugs, but I tested with logs that changes were reflected.
I will check if it works with other versions of iPhone (not 11)

@mrousavy
Copy link
Owner

mrousavy commented Jul 2, 2024

Hey all! I just spent some time to build a new feature: I created a PR to add a isMirrored prop to Camera: #3036

With this prop you can enable or disable output mirroring 🎉🥳

If you appreciate my time and dedication towards building new features and constantly improving this library, please consider sponsoring me on GitHub :)

@CyxouD
Copy link

CyxouD commented Jul 3, 2024

@mrousavy thank you for introducing new features! However, in this issue, we needed Preview to be not mirrored in the front camera, but it isn't supported:

To avoid dizzyness for users, the Preview will always be mirrored in the front Camera, but not in the back Camera. This is the same behaviour as in the native iOS Camera app.

@mrousavy
Copy link
Owner

mrousavy commented Jul 3, 2024

This is intentional - if the preview was mirrored the app would be pretty unusable because every movement goes in the wrong direction. All camera apps mirror the front preview.

If you really really need the preview not mirrored for whatever reason, you can just apply a scaleX: -1 transform to the <Camera> view style.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
💭 question Further information is requested
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants