Skip to content

set Camera lanscape #2034

Description

@thegobot

Is it possible to set the camera to landscape mode when the screen is in portrait orientation?

#407

Currently, to shoot wide videos, I have to rotate the phone. I need 720x480 in portrait mode (so there are no black bars on the sides). I've tried everything, but the preview keeps stretching.

val stream = GenericStream(baseContext, this, CameraXSource(), NoAudioSource()).apply {
    getGlInterface().autoHandleOrientation = false
    getGlInterface().setIsPortrait(false)
    getGlInterface().setCameraOrientation(0)
}

stream.prepareVideo(
        width = 720,
        height = 480,
        rotation = 0,
)

stream.startPreview()

``

What else needs to be done to get the camera in 720x480 resolution in portrait mode?

Activity

  1. pedroSG94 commented on Feb 9, 2026

    @pedroSG94
    Owner

    Hello,

    The sensor in all devices is a rectangle so you can't do it. You have 3 options.
    1 - Black borders
    2 - Always landscape or portrait resolution but when you rotate the device the result also rotate (same that default camera apps)
    3 - Cropping the image. Not recommended because you will lose part of the image.

    By the default the app example use the first case. You can use the second case like this:

       GenericStream(requireContext(), this).apply {
          getGlInterface().autoHandleOrientation = false
          // this is if you start activity in portrait. Test with 0, 90 180 or 270
          val orientation = 270 
          getGlInterface().setStreamRotation(orientation)
          getGlInterface().setPreviewRotation(orientation)
        }

    Case 3 is not implemented by default but you can get the same result using case 2 + filters. To do it you need modify RotationFilter:

    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
    public class RotationFilterRender extends BaseFilterRender {
    
      //rotation matrix
      private final float[] squareVertexDataFilter = {
          // X, Y, Z, U, V
          -1f, -1f, 0f, 0f, 0f, //bottom left
          1f, -1f, 0f, 1f, 0f, //bottom right
          -1f, 1f, 0f, 0f, 1f, //top left
          1f, 1f, 0f, 1f, 1f, //top right
      };
    
      private int program = -1;
      private int aPositionHandle = -1;
      private int aTextureHandle = -1;
      private int uMVPMatrixHandle = -1;
      private int uSTMatrixHandle = -1;
      private int uSamplerHandle = -1;
    
      private int rotation = 0;
      private boolean horizontalFlip = false;
      private boolean verticalFlip = false;
    
      public RotationFilterRender() {
        squareVertex = ByteBuffer.allocateDirect(squareVertexDataFilter.length * FLOAT_SIZE_BYTES)
            .order(ByteOrder.nativeOrder())
            .asFloatBuffer();
        squareVertex.put(squareVertexDataFilter).position(0);
        Matrix.setIdentityM(MVPMatrix, 0);
        Matrix.setIdentityM(STMatrix, 0);
      }
    
      @Override
      protected void initGlFilter(Context context) {
        String vertexShader = GlUtil.getStringFromRaw(context, R.raw.simple_vertex);
        String fragmentShader = GlUtil.getStringFromRaw(context, R.raw.simple_fragment);
    
        program = GlUtil.createProgram(vertexShader, fragmentShader);
        aPositionHandle = GLES20.glGetAttribLocation(program, "aPosition");
        aTextureHandle = GLES20.glGetAttribLocation(program, "aTextureCoord");
        uMVPMatrixHandle = GLES20.glGetUniformLocation(program, "uMVPMatrix");
        uSTMatrixHandle = GLES20.glGetUniformLocation(program, "uSTMatrix");
        uSamplerHandle = GLES20.glGetUniformLocation(program, "uSampler");
      }
    
      @Override
      protected void drawFilter() {
        GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
    
        GLES20.glUseProgram(program);
    
        squareVertex.position(SQUARE_VERTEX_DATA_POS_OFFSET);
        GLES20.glVertexAttribPointer(aPositionHandle, 3, GLES20.GL_FLOAT, false,
            SQUARE_VERTEX_DATA_STRIDE_BYTES, squareVertex);
        GLES20.glEnableVertexAttribArray(aPositionHandle);
    
        squareVertex.position(SQUARE_VERTEX_DATA_UV_OFFSET);
        GLES20.glVertexAttribPointer(aTextureHandle, 2, GLES20.GL_FLOAT, false,
            SQUARE_VERTEX_DATA_STRIDE_BYTES, squareVertex);
        GLES20.glEnableVertexAttribArray(aTextureHandle);
    
        GLES20.glUniformMatrix4fv(uMVPMatrixHandle, 1, false, MVPMatrix, 0);
        GLES20.glUniformMatrix4fv(uSTMatrixHandle, 1, false, STMatrix, 0);
    
        GLES20.glUniform1i(uSamplerHandle, 0);
        GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, previousTexId);
      }
    
      @Override
      protected void disableResources() {
        GlUtil.disableResources(aTextureHandle, aPositionHandle);
      }
    
      @Override
      public void release() {
        GLES20.glDeleteProgram(program);
      }
    
      public int getRotation() {
        return rotation;
      }
    
      public void setRotation(int rotation) {
        this.rotation = rotation;
        updateMatrix();
      }
    
      /**
       * Keep aspect ratio if you rotate 90º or 270º.
       * @param rotation value
       * @param width width of stream (prepareVideo method) if you are streaming or preview (startPreview method) if you aren't streaming.
       * @param height height of stream (prepareVideo method) if you are streaming or preview (startPreview method) if you aren't streaming.
       */
      public void setRotationFixed(int rotation, int width, int height, boolean isPortrait, boolean crop) {
        this.rotation = rotation;
        //Set rotation
        Matrix.setRotateM(MVPMatrix, 0, rotation, 0, 0, 1.0f);
        if (rotation == 90 || rotation == 270) {
          if (crop) {
              float value = (float) width / (float) height;
              if (isPortrait) {
                  Matrix.scaleM(MVPMatrix, 0, 1f, 1f + (value - 1f) * 2f, 0f);
              } else {
                  Matrix.scaleM(MVPMatrix, 0, 1f + (value - 1f) * 2f, 1f, 0f);
              }
          } else {
              float value = (float) height / (float) width;
              if (isPortrait) {
                  Matrix.scaleM(MVPMatrix, 0, value, 1f, 0f);
              } else {
                  Matrix.scaleM(MVPMatrix, 0, 1f, value, 0f);
              }
          }
        } else {
          Matrix.scaleM(MVPMatrix, 0, 1f, 1f, 0f);
        }
      }
    
      public void flip(boolean horizontalFlip, boolean verticalFlip) {
        this.horizontalFlip = horizontalFlip;
        this.verticalFlip = verticalFlip;
        updateMatrix();
      }
    
      private void updateMatrix() {
        Matrix.setIdentityM(MVPMatrix, 0);
        Matrix.scaleM(MVPMatrix, 0, horizontalFlip ? -1f : 1f, verticalFlip ? -1f : 1f, 1f);
        Matrix.rotateM(MVPMatrix, 0, rotation, 0f, 0f, -1f);
      }
    }
    

    Then you can use the filter like this:

        GenericStream(requireContext(), this).apply {
          getGlInterface().autoHandleOrientation = false
          // this is if you start activity in portrait. Test with 0, 90 180 or 270 depend of the orientation when you start the activity
          val orientation = 270
          getGlInterface().setStreamRotation(orientation)
          getGlInterface().setPreviewRotation(orientation)
          //the filter must be removed when you rotate the device. You only need it on portrait.
          getGlInterface().addFilter(RotationFilterRender().apply {
            setRotationFixed(orientation, 640, 480, true, true)
          })
        }

    All apps works using case 1 or 2 so the user understand that to get a landscape image must rotate the device. I think that you should use one of that cases to have an intuitive app.

    If you want use other case. Explain it in detail because I don't understand your case.
    I think that you want something similar to the current iphone 17. It is using the case 3.

  2. thegobot commented on Feb 12, 2026

    @thegobot
    Author

    Thanks, friend!

    I got landscape mode working with your example. But the camera still capture in portrait mode. It doesn't expand or capture a large horizontal area.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions