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

PICO 4 Ultra Compatibility? #5659

Closed
Sid-univ opened this issue Dec 12, 2024 · 13 comments
Closed

PICO 4 Ultra Compatibility? #5659

Sid-univ opened this issue Dec 12, 2024 · 13 comments

Comments

@Sid-univ
Copy link

Environment

  • OS: Windows
  • Scrcpy version: 3.1
  • Installation method: Running command using "open_a_terminal_here.bat"
  • Device model: PICO 4 Ultra
  • Android version: 14

Describe the bug

Trying to run a simple 'scrcpy' command, device is found but got errors (Using Meta Quest 3 has no problem).
Using version 2.7 works but using crop parameters do not works (What ever I set, it renders always the 2 eyes)

C:\Users\xxxxxx\Downloads\scrcpy-win64-v3.1>scrcpy
scrcpy 3.1 <https://github.com/Genymobile/scrcpy>
INFO: ADB device found:
INFO:     -->   (usb)  PA92Y0MGJ9030219G               device  A9210
C:\Users\xxxxxx\Downloads\scrcpy-win64-v3.1\scrcpy-serv...file pushed, 0 skipped. 106.0 MB/s (90640 bytes in 0.001s)
[server] INFO: Device: [Pico] Pico A92Y0 (Android 14)
[server] ERROR: Exception on thread Thread[video,5,main]
java.lang.AssertionError: java.lang.reflect.InvocationTargetException
        at com.genymobile.scrcpy.wrappers.DisplayManager.getDisplayInfo(DisplayManager.java:134)
        at com.genymobile.scrcpy.video.ScreenCapture.prepare(ScreenCapture.java:69)
        at com.genymobile.scrcpy.video.SurfaceEncoder.streamCapture(SurfaceEncoder.java:78)
        at com.genymobile.scrcpy.video.SurfaceEncoder.lambda$start$0$com-genymobile-scrcpy-video-SurfaceEncoder(SurfaceEncoder.java:296)
        at com.genymobile.scrcpy.video.SurfaceEncoder$$ExternalSyntheticLambda0.run(D8$$SyntheticClass:0)
        at java.lang.Thread.run(Thread.java:1012)
Caused by: java.lang.reflect.InvocationTargetException
        at java.lang.reflect.Method.invoke(Native Method)
        at com.genymobile.scrcpy.wrappers.DisplayManager.getDisplayInfo(DisplayManager.java:119)
        ... 5 more
Caused by: java.lang.NullPointerException: Attempt to read from field 'android.app.WindowConfiguration android.content.res.Configuration.windowConfiguration' on a null object reference in method 'void android.hardware.display.ExtDisplayManagerGlobalImpl.adjustDisplayInfo(int, android.view.DisplayInfo)'
        at android.hardware.display.ExtDisplayManagerGlobalImpl.adjustDisplayInfo(ExtDisplayManagerGlobalImpl.java:43)
        at android.hardware.display.DisplayManagerGlobal.getDisplayInfoLocked(DisplayManagerGlobal.java:208)
        at android.hardware.display.DisplayManagerGlobal.getDisplayInfo(DisplayManagerGlobal.java:184)
        ... 7 more
[server] ERROR: Audio encoding error
java.io.IOException: android.system.ErrnoException: write failed: EPIPE (Broken pipe)
        at com.genymobile.scrcpy.util.IO.write(IO.java:28)
        at com.genymobile.scrcpy.util.IO.writeFully(IO.java:37)
        at com.genymobile.scrcpy.util.IO.writeFully(IO.java:59)
        at com.genymobile.scrcpy.device.Streamer.writeDisableStream(Streamer.java:66)
        at com.genymobile.scrcpy.audio.AudioEncoder.encode(AudioEncoder.java:244)
        at com.genymobile.scrcpy.audio.AudioEncoder.lambda$start$0$com-genymobile-scrcpy-audio-AudioEncoder(AudioEncoder.java:133)
        at com.genymobile.scrcpy.audio.AudioEncoder$$ExternalSyntheticLambda3.run(D8$$SyntheticClass:0)
        at java.lang.Thread.run(Thread.java:1012)
Caused by: android.system.ErrnoException: write failed: EPIPE (Broken pipe)
        at libcore.io.Linux.writeBytes(Native Method)
        at libcore.io.Linux.write(Linux.java:288)
        at libcore.io.ForwardingOs.write(ForwardingOs.java:938)
        at libcore.io.BlockGuardOs.write(BlockGuardOs.java:442)
        at android.system.Os.write(Os.java:987)
        at com.genymobile.scrcpy.util.IO.write(IO.java:25)
        ... 7 more
INFO: Renderer: direct3d
WARN: Device disconnected
ERROR: Demuxer 'video': stream disabled due to connection error
ERROR: Demuxer 'audio': stream disabled due to connection error
@rom1v
Copy link
Collaborator

rom1v commented Dec 12, 2024

Please post your framework.jar:

adb pull /system/framework/framework.jar

@Sid-univ
Copy link
Author

framework.zip

@rom1v
Copy link
Collaborator

rom1v commented Dec 12, 2024

Please replace this file in your scrcpy 3.1:

  • scrcpy-server SHA-256: d64b24da33c0d9c670e79df5ec7e7c3737fed195131cb6547c7a0fc77d432ab

Does it solve the problem?

diff --git server/src/main/java/com/genymobile/scrcpy/Workarounds.java server/src/main/java/com/genymobile/scrcpy/Workarounds.java
index eec00a046..cbe414f4f 100644
--- server/src/main/java/com/genymobile/scrcpy/Workarounds.java
+++ server/src/main/java/com/genymobile/scrcpy/Workarounds.java
@@ -10,6 +10,7 @@ import android.content.AttributionSource;
 import android.content.Context;
 import android.content.ContextWrapper;
 import android.content.pm.ApplicationInfo;
+import android.content.res.Configuration;
 import android.media.AudioAttributes;
 import android.media.AudioManager;
 import android.media.AudioRecord;
@@ -139,6 +140,10 @@ public final class Workarounds {
             Field configurationControllerField = ACTIVITY_THREAD_CLASS.getDeclaredField("mConfigurationController");
             configurationControllerField.setAccessible(true);
             configurationControllerField.set(ACTIVITY_THREAD, configurationController);
+
+            Field configurationField = configurationControllerClass.getDeclaredField("mConfiguration");
+            configurationField.setAccessible(true);
+            configurationField.set(configurationController, new Configuration());
         } catch (Throwable throwable) {
             Ln.d("Could not fill configuration: " + throwable.getMessage());
         }

@Sid-univ
Copy link
Author

Sid-univ commented Dec 13, 2024

It doesn't solved the problem, that's the new error

C:\Users\xxxxxx\Downloads\scrcpy-win64-v3.1>scrcpy
scrcpy 3.1 <https://github.com/Genymobile/scrcpy>
INFO: ADB device found:
INFO:     -->   (usb)  PA92Y0MGJ9030219G               device  A9210
C:\Users\xxxxxx\Downloads\scrcpy-win64-v3.1\scrcpy-serv...file pushed, 0 skipped. 135.4 MB/s (90696 bytes in 0.001s)
[server] INFO: Device: [Pico] Pico A92Y0 (Android 14)
[server] ERROR: Capture/encoding error: java.lang.IllegalArgumentException:
[server] ERROR: Exception on thread Thread[video,5,main]
java.lang.IllegalArgumentException:
        at android.media.MediaCodec.native_configure(Native Method)
        at android.media.MediaCodec.configure(MediaCodec.java:2336)
        at android.media.MediaCodec.configure(MediaCodec.java:2252)
        at com.genymobile.scrcpy.video.SurfaceEncoder.streamCapture(SurfaceEncoder.java:92)
        at com.genymobile.scrcpy.video.SurfaceEncoder.lambda$start$0$com-genymobile-scrcpy-video-SurfaceEncoder(SurfaceEncoder.java:296)
        at com.genymobile.scrcpy.video.SurfaceEncoder$$ExternalSyntheticLambda0.run(D8$$SyntheticClass:0)
        at java.lang.Thread.run(Thread.java:1012)
INFO: Renderer: direct3d
WARN: Device disconnected
ERROR: Demuxer 'audio': stream disabled due to connection error
ERROR: Invalid video size: 0x0

@rom1v
Copy link
Collaborator

rom1v commented Dec 13, 2024

For reference, the problem is that the vendor modified the system in DisplayManagerGlobal.getDisplayInfoLocked() to add a call to a custom implementation:

    @Override // android.hardware.display.IExtDisplayManagerGlobal
    public void adjustDisplayInfo(int displayId, DisplayInfo displayInfo) {
        ActivityThread thread;
        if (displayId != 0 || ActivityThread.isSystem() || (thread = ActivityThread.currentActivityThread()) == null) {
            return;
        }
        Configuration configuration = thread.getConfiguration();
        if (configuration == null) {
            configuration = thread.getExt().getInitConfiguration();
        }
        Rect bound = configuration.windowConfiguration.getBounds();
        int width = bound.width();
        displayInfo.logicalWidth = width;
        displayInfo.appWidth = width;
        displayInfo.largestNominalAppWidth = width;
        displayInfo.smallestNominalAppWidth = width;
        int height = bound.height();
        displayInfo.logicalHeight = height;
        displayInfo.appHeight = height;
        displayInfo.largestNominalAppHeight = height;
        displayInfo.smallestNominalAppHeight = height;
        int i = configuration.densityDpi;
        displayInfo.logicalDensityDpi = i;
        float f = i;
        displayInfo.physicalYDpi = f;
        displayInfo.physicalXDpi = f;
        displayInfo.rotation = 0;
    }

Setting an empty configuration as I did does not work, because then the size is 0x0.

The configuration is (a priori) initially null, so their call thread.getExt().getInitConfiguration(); is not sufficient to initialize it so that configuration.windowConfiguration is not null.

Another workaround would be to NOT initialize Activity.currentActivityThread(), but that's required for other workarounds.

Maybe we can just make ActivityThread.isSystem() return true. Let's try.

diff --git server/src/main/java/com/genymobile/scrcpy/Workarounds.java server/src/main/java/com/genymobile/scrcpy/Workarounds.java
index eec00a046..a6caf59b1 100644
--- server/src/main/java/com/genymobile/scrcpy/Workarounds.java
+++ server/src/main/java/com/genymobile/scrcpy/Workarounds.java
@@ -42,6 +42,11 @@ public final class Workarounds {
             Field sCurrentActivityThreadField = ACTIVITY_THREAD_CLASS.getDeclaredField("sCurrentActivityThread");
             sCurrentActivityThreadField.setAccessible(true);
             sCurrentActivityThreadField.set(null, ACTIVITY_THREAD);
+
+            // activityThread.mSystemThread = true;
+            Field mSystemThreadField = ACTIVITY_THREAD_CLASS.getDeclaredField("mSystemThread");
+            mSystemThreadField.setAccessible(true);
+            mSystemThreadField.setBoolean(ACTIVITY_THREAD, true);
         } catch (Exception e) {
             throw new AssertionError(e);
         }

Please test this new binary:

  • scrcpy-server SHA-256: 2f9aa1ec85af630f8f5fce608770bc70ac175cab280b76241666eb48dacc9a5

@Sid-univ
Copy link
Author

Thank you! It works perfectly fine!
I have a last question, Sir!
Crop parameter seems to not work exactly the same on PICO and Meta Quest 2/3 (I attach 2 screenshots):
METACRPOP
PICO_CROP
Do you know if there is an option to make the --crop parameter on PICO work the same way as it does on Quest? (full screen of the rendered thing inside the window)

@rom1v
Copy link
Collaborator

rom1v commented Dec 13, 2024

--crop expects 4 values: --crop=width:height:x:y.

If the screen content is not the same for different devices, you must crop another rectangle by adjusting the width/height/x/y values.

@Sid-univ
Copy link
Author

Ho ok my bad! It was cause of the display id used before to make it works (I was using --display-id=1) without it it works perfectly!
Thank you so much for your time! Have a nice day! ;)

@rom1v
Copy link
Collaborator

rom1v commented Dec 13, 2024

I was using --display-id=1

Are there several displays on Quest or Pico 4?

scrcpy --list-displays

@Sid-univ
Copy link
Author

[server] INFO: Device: [Pico] Pico A92Y0 (Android 14)
[server] INFO: List of displays:
--display-id=0 (4320x2160)
--display-id=2 (1020x540)
--display-id=14 (825x750)

@rom1v
Copy link
Collaborator

rom1v commented Dec 13, 2024

Oh, ok, because you're currently mirroring? These displays are not present when no scrcpy is running, right?

@Sid-univ
Copy link
Author

Sid-univ commented Dec 13, 2024

Yes, I'm currently mirroring! I believe they are not in use without scrcpy!
In the screenshot I provided earlier, I'm using --crop=2160:2160:0:0 for PICO and --crop=1600:900:2017:510 for Meta Quest.
The difference between them is that PICO's rendering has black lines on each side instead of having a fully rendered screen.

rom1v added a commit that referenced this issue Dec 14, 2024
Make ActivityThread.isSystem() return true to avoid a
NullPointerException later.

Refs #5659 comment <#5659 (comment)>
Fixes #5659 <#5659>
@rom1v
Copy link
Collaborator

rom1v commented Dec 14, 2024

Merged into dev: dc2fcc4 🚀

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