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

Fix incorrect capture when using DisplayManager API #4840

Open
wants to merge 1 commit into
base: dev
Choose a base branch
from

Conversation

eiyooooo
Copy link
Contributor

@eiyooooo eiyooooo commented Apr 12, 2024

Fix the incorrect capture like this
image

@rom1v
Copy link
Collaborator

rom1v commented Apr 12, 2024

Thank you for your contribution.

Fix the same incorrect capture occur before 7e3b935 when using DisplayManager API

The commit 7e3b935 destroyed/created a new display on every rotation. On the contrary, your PR keeps the virtual display instance (instead of releasing/creating a new one).

Could you please explain a bit more the problem?

@eiyooooo
Copy link
Contributor Author

Could you please explain a bit more the problem?

When execute SurfaceControl.createDisplay on Android 14, it essentially calls DisplayManager.createVirtualDisplay.
When execute SurfaceControl.setDisplayProjection on Android 14, it essentially calls DisplayManager.resizeVirtualDisplay.
When execute SurfaceControl.setDisplayLayerStack on Android 14, it essentially calls DisplayManager.setDisplayIdToMirror.

Instead of releasing/creating a new VirtualDisplay (avoiding displayId increase), I do the same job at

// Density doesn't matter since this virtual display is only used for mirroring.
virtualDisplay.resize(videoRect.width(), videoRect.height(), 1);
virtualDisplay.setSurface(surface);

When executing DisplayManager.setDisplayIdToMirror, it will copy the configuration of the original display to the created display (including the rotation). (It probably can only happen once to each created display, so you need to destroy the created display and create a new one to copy the rotation at 7e3b935.)

But if we use DisplayManager API directly create a VirtualDisplay and set it to VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR, orientation and displayRect will automatically be computed based on configuration changes. 

Because Android 14 lock orientation doesn't work as expected <#4011>, so we use videoRect. But videoRect is already rotated according to the device rotation, so we need to freeze the VirtualDisplay's rotation to 0 to avoid a double rotation.

(It seems a little bit hard to understand while I am poor at English writing 😴)

And I suggest use DisplayManager API directly on Android 14 and above, instead of trying the SurfaceControl API first, because it basically does the same thing. If we use DisplayManager API, we could have a better control of the created VirtualDisplay.

@rom1v
Copy link
Collaborator

rom1v commented Apr 17, 2024

Thank you for the detailed answer. I will read in details a bit later.

Just a quick question:

Instead of releasing/creating a new VirtualDisplay (avoiding displayId increase)

Why is it a problem that an internal id is incremented? IIUC it's not the "display id" as in scrcpy --display-id=, is it?

@eiyooooo
Copy link
Contributor Author

IIUC it's not the "display id" as in scrcpy --display-id=, is it?

Nope, it's the "display id" as in scrcpy --display-id=😉

@eiyooooo
Copy link
Contributor Author

Why is it a problem that an internal id is incremented?

I remember someone mentioning that when the "display ID" is over 1000, the system may encounter issues and need a reboot to fix it.

@rom1v
Copy link
Collaborator

rom1v commented Apr 17, 2024

Oh, I'm had never noticed that mirroring the device created a new display id (listed by scrcpy --list-displays) with createVirtualDisplay() (be3f949).

Maybe there is another method which does not create a new display id (maybe refs #4848, but I did not understand the issue).

@eiyooooo
Copy link
Contributor Author

Oh, I'm had never noticed that mirroring the device created a new display id (listed by scrcpy --list-displays) with createVirtualDisplay()

BTW, in Android 14

also created a new display id

image

@rom1v
Copy link
Collaborator

rom1v commented Apr 17, 2024

Indeed (but I think this is true only for Android 14, on my older devices it does not create a new display id).

@rom1v
Copy link
Collaborator

rom1v commented Apr 17, 2024

With your commit from this PR, and this additional hack to force using the DisplayManager API:

diff --git a/server/src/main/java/com/genymobile/scrcpy/ScreenCapture.java b/server/src/main/java/com/genymobile/scrcpy/ScreenCapture.java
index 6f9c4d338..d04fad572 100644
--- a/server/src/main/java/com/genymobile/scrcpy/ScreenCapture.java
+++ b/server/src/main/java/com/genymobile/scrcpy/ScreenCapture.java
@@ -41,6 +41,7 @@ public class ScreenCapture extends SurfaceCapture implements Device.RotationList
         }
 
         try {
+            if (true) throw new Exception("fake");
             display = createDisplay();
             setDisplaySurface(display, surface, videoRotation, contentRect, unlockedVideoRect, layerStack);
             Ln.d("Display: using SurfaceControl API");

I can reproduce the problem that was fixed by 7e3b935 on a Pixel 8. 😞

@eiyooooo
Copy link
Contributor Author

I can reproduce the problem that was fixed by 7e3b935 on a Pixel 8. 😞

Could you please post the screenshot of the incorrect capture?

@rom1v
Copy link
Collaborator

rom1v commented Apr 17, 2024

Could you please post the screenshot of the incorrect capture?

In fact, it is not a corrupted image: on device rotation, sometimes (not always, so there's probably a race condition) it just stops sending frames (so the physical device is portrait and the screen is updated, but the scrcpy window is landscape with the last landscape image, frozen), and the video stream is restored when I rotate the device screen one more time.

In practice, I can only reproduce if the current active application is the (default) camera app.

@eiyooooo
Copy link
Contributor Author

on device rotation, sometimes (not always, so there's probably a race condition) it just stops sending frames (so the physical device is portrait and the screen is updated, but the scrcpy window is landscape with the last landscape image, frozen), and the video stream is restored when I rotate the device screen one more time.

int displayId = virtualDisplay.getDisplay().getDisplayId();
ServiceManager.getWindowManager().freezeRotation(displayId, 0);

I think this should fix it 😑

@rom1v
Copy link
Collaborator

rom1v commented Apr 17, 2024

Nope, with or without these lines, I can reproduce the problem.

Besides, this is probably not an "incorrect" rotation value, it looks like a race condition (80% of times, it works correctly…).

@eiyooooo
Copy link
Contributor Author

Fix the same incorrect capture occur before 7e3b935 when using DisplayManager API

So this pr is not about the incorrect capture occur before 7e3b935

Here are the incorrect capture I met

image

@rom1v
Copy link
Collaborator

rom1v commented Apr 17, 2024

😮 Do you encounter this problem with current dev branch on rotation, without passing --crop or --lock-video-orientation?

@eiyooooo
Copy link
Contributor Author

eiyooooo commented Apr 17, 2024

😮 Do you encounter this problem with current dev branch on rotation, without passing --crop or --lock-video-orientation?

When using DisplayManager API without

int displayId = virtualDisplay.getDisplay().getDisplayId();
ServiceManager.getWindowManager().freezeRotation(displayId, 0);

this problem occurs occasionally.

(without passing --crop or --lock-video-orientation)

@eiyooooo
Copy link
Contributor Author

I can reproduce the problem that was fixed by 7e3b935 on a Pixel 8. 😞

With my commit from this PR, I can reproduce your bug fixed by 7e3b935 too. 😥

So I think releasing/creating a new one still necessary.

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

Successfully merging this pull request may close these issues.

None yet

2 participants