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

Rotation issue for RTMP stream in portrait mode (from surface, off-screen) #1647

Closed
FahimHT opened this issue Nov 21, 2024 · 2 comments
Closed

Comments

@FahimHT
Copy link

FahimHT commented Nov 21, 2024

I'm doing RTMP stream from a surface. The problem is, when the phone is in portrait mode, I can't figure out how to rotate the stream by 90 degrees.

Currently I have an encoder, I'm passing its input surface to the capture request builder. It works in landscape mode.

// Note that if I swap width, height below to set portrait mode, capture session creation fails with an exception
MediaFormat videoFormat = MediaFormat.createVideoFormat(MIME_TYPE, mWidth, mHeight);
videoEncoder = MediaCodec.createEncoderByType(MIME_TYPE);
videoEncoder.configure(videoFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
Surface streamSurface = videoEncoder.createInputSurface();
//..
CaptureRequest.Builder builder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);
builder.addTarget(streamSurface);

I've checked all previously reported issues here related to rotation, but either couldn't make the solutions from those work, or they weren't applicable since I'm not using a preview display.

Tried below code to rotate using OpenGL before passing to the encoder, but couldn't make it work. I'm not sure if this is the proper way, but it throws an exception in r.initGl() with message could not compile shader 35633 (tested on multiple phones).

CameraRender r = new CameraRender();
r.setRotation(90);
r.initGl(640, 480, recordingService, 0,0); // Tried 480, 640 too as I'm not sure which orientation this surface should be
r.draw();
streamSurface = r.getSurface();
videoEncoder.setInputSurface(streamSurface);
//..
CaptureRequest.Builder builder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);
builder.addTarget(streamSurface);

Tried OpenGlView too but since I'm recording off-screen, surfaceCreated() is never invoked.

OpenGlView view = new OpenGlView(recordingService);
view.setRotation(90);
view.setStreamRotation(90);
view.addMediaCodecSurface(videoEncoder.createInputSurface());
view.getHolder().addCallback(new SurfaceHolder.Callback() {
    @Override
    public void surfaceCreated(@NonNull SurfaceHolder holder) {
        streamSurface = view.getSurface();
        // ..
        CaptureRequest.Builder builder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);
        builder.addTarget(streamSurface);
    }
});
view.setForceRender(true);
view.start();

I know that if I record to file, I can set media-recorder hint to rotate the output file, but that doesn't apply for streaming.

videoFormat.setInteger(MediaFormat.KEY_ROTATION, 90) doesn't work since according to doc it'll only work if I use an output surface, but then I'll have to read each frame from the output surface and convert those in order to be able to stream. This seems inefficient as instead of using:

@Override
public void onOutputBufferAvailable(@NonNull MediaCodec codec, int index, @NonNull MediaCodec.BufferInfo bufferInfo) { //.. }

I'll then have to listen for each frame of that output surface and process those.

Due to other requirements I can't directly stream from Camera2 using Root Encoder.

Any ideas on how to proceed?

@FahimHT FahimHT closed this as completed Nov 21, 2024
@FahimHT FahimHT reopened this Nov 21, 2024
@pedroSG94
Copy link
Owner

pedroSG94 commented Nov 21, 2024

Hello,

In your case you only can use GlStreamInterface. Code example:

val glInterface = GlStreamInterface(context)

/**start*/
glInterface.setEncoderSize(width, height)
glInterface.setIsPortrait(isPortrait) //should be used, by default false
glInterface.setCameraOrientation(rotation) //should be used, by default 0, you can use this value to rotate on fly
glInterface.forceOrientation(videoSource.getOrientationConfig()) //optional, force landscape or portrait no matter the rotation value. In most of cases you can ignore it.
glInterface.start()
 //Send glInterface to the camera, the camera render the surface of glInterface
builder.addTarget(Surface(glInterface.surfaceTexture))
//Add surface of videoEncoder to glInteface, the data received in the glInterface.surfaceTexture is copied into videoEncoder.inputSurface and you can use opengl to modify it as you want
glInterface.addMediaCodecSurface(videoEncoder.inputSurface)


/**stop*/
glInterface.removeMediaCodecSurface()
glInterface.stop()

The code example is from this methods:
https://github.com/pedroSG94/RootEncoder/blob/master/library/src/main/java/com/pedro/library/base/StreamBase.kt#L110
https://github.com/pedroSG94/RootEncoder/blob/master/library/src/main/java/com/pedro/library/base/StreamBase.kt#L490

https://github.com/pedroSG94/RootEncoder/blob/master/library/src/main/java/com/pedro/library/base/StreamBase.kt#L504

This allow you rotate the image, you can even use filters or add a preview on fly when you want.
You have a rotation filter if it is necessary

@FahimHT
Copy link
Author

FahimHT commented Nov 21, 2024

Thank you so much!
Stream is now working with correct orientation.

@FahimHT FahimHT closed this as completed Nov 21, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants