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

Scrcpy interaction with OPPO Phone is not working #1518

Open
EloZ27 opened this issue Jun 17, 2020 · 71 comments
Open

Scrcpy interaction with OPPO Phone is not working #1518

EloZ27 opened this issue Jun 17, 2020 · 71 comments

Comments

@EloZ27
Copy link

EloZ27 commented Jun 17, 2020

I have tried scrcpy and various guidelines for mirroring phones via scrcpy but it seems that the interaction with keyboard and mouse and audio does not work with my OPPO F11.

Help

@rom1v
Copy link
Collaborator

rom1v commented Jun 17, 2020

@EloZ27
Copy link
Author

EloZ27 commented Jun 17, 2020

Screenshot_2020-06-18-02-20-10-96

Yes, I have already read that. But based in my phone I don't have such option for that. I even looked entirely in my phone, but I haven't found any option for it.

@rom1v
Copy link
Collaborator

rom1v commented Jun 17, 2020

Any hint in adb logcat?

What happens if you focus some text area on your device, and execute adb shell input text hello from your computer?

@EloZ27
Copy link
Author

EloZ27 commented Jun 17, 2020

Cant seem to find any hint in the logcat.

adb shell input text hello worked

@rom1v
Copy link
Collaborator

rom1v commented Jun 17, 2020

adb shell input text hello worked

Oh, that's very weird, then. Pressing h e l l o in the scrcpy windows does not work?

What if you start with scrcpy --prefer-text?

@EloZ27
Copy link
Author

EloZ27 commented Jun 18, 2020

123123123123

Hello. Here is the result for the scrcpy --prefer-text.

@rom1v
Copy link
Collaborator

rom1v commented Jun 18, 2020

But what happens when you enter text in the scrcpy window?

@EloZ27
Copy link
Author

EloZ27 commented Jun 18, 2020

Hmmm. Somehow, typing in the keyboard worked in scrcpy. But the mouse still does not interact

@rom1v
Copy link
Collaborator

rom1v commented Jun 18, 2020

Probably similar to #1347.

Does this work: #1347 (comment)

@EloZ27
Copy link
Author

EloZ27 commented Jun 18, 2020

Yea. It some how tapped my screen.

@rom1v
Copy link
Collaborator

rom1v commented Jun 18, 2020

Please test with older versions of scrcpy, if you find one which work, it will help a lot to find the cause.

@EloZ27
Copy link
Author

EloZ27 commented Jun 18, 2020

I have tried versions down to v1.4, scroll wheel and left mouse button does not work. Middle click and right click worked as well as the keyboard.

@GetCurious
Copy link

GetCurious commented Jul 5, 2020

scrcpy 1.12.1

dependencies:

  • SDL 2.0.10
  • libavcodec 58.54.100
  • libavformat 58.29.100
  • libavutil 56.31.100

adb shell input tap 179 179

log.txt

same phone (F11 Pro), same situation, most keyboard, mid click & right click works except left click.

@rom1v
Copy link
Collaborator

rom1v commented Jul 5, 2020

adb shell input tap 179 179

Does it work or not?

@GetCurious
Copy link

GetCurious commented Jul 6, 2020

now running build master version

scrcpy 1.14

dependencies:

  • SDL 2.0.10
  • libavcodec 58.54.100
  • libavformat 58.29.100
  • libavutil 56.31.100

the command works. but left click doesnt.

@rom1v
Copy link
Collaborator

rom1v commented Jul 6, 2020

Oh, could you test with this change, please:

diff --git a/app/src/input_manager.c b/app/src/input_manager.c
index 9c22ee0a..7d500efe 100644
--- a/app/src/input_manager.c
+++ b/app/src/input_manager.c
@@ -511,7 +511,8 @@ convert_mouse_button(const SDL_MouseButtonEvent *from, struct screen *screen,
     to->inject_touch_event.position.screen_size = screen->frame_size;
     to->inject_touch_event.position.point =
         screen_convert_window_to_frame_coords(screen, from->x, from->y);
-    to->inject_touch_event.pressure = 1.f;
+    to->inject_touch_event.pressure =
+        from->type == SDL_MOUSEBUTTONDOWN ? 1.f : 0.f;
     to->inject_touch_event.buttons =
         convert_mouse_buttons(SDL_BUTTON(from->button));
 

@GetCurious
Copy link

GetCurious commented Jul 7, 2020

convert_mouse_button(const SDL_MouseButtonEvent *from, struct screen *screen,
                     struct control_msg *to) {
    to->type = CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT;

    if (!convert_mouse_action(from->type, &to->inject_touch_event.action)) {
        return false;
    }

    to->inject_touch_event.pointer_id = POINTER_ID_MOUSE;
    to->inject_touch_event.position.screen_size = screen->frame_size;
    to->inject_touch_event.position.point =
        screen_convert_to_frame_coords(screen, from->x, from->y);
    to->inject_touch_event.pressure =
        from->type == SDL_MOUSEBUTTONDOWN ? 1.f : 0.f;
    to->inject_touch_event.buttons =
        convert_mouse_buttons(SDL_BUTTON(from->button));

    return true;
}

no difference.
can i have your full input_manager.c?

@rom1v
Copy link
Collaborator

rom1v commented Jul 7, 2020

no difference.

Could you please test this change instead (or in addition), on dev:

diff --git a/server/src/main/java/com/genymobile/scrcpy/Controller.java b/server/src/main/java/com/genymobile/scrcpy/Controller.java
index 71e7ec9c..1f249225 100644
--- a/server/src/main/java/com/genymobile/scrcpy/Controller.java
+++ b/server/src/main/java/com/genymobile/scrcpy/Controller.java
@@ -11,7 +11,7 @@ import java.io.IOException;
 
 public class Controller {
 
-    private static final int DEVICE_ID_VIRTUAL = -1;
+    private static final int DEVICE_ID_VIRTUAL = 0;
 
     private final Device device;
     private final DesktopConnection connection;

can i have your full input_manager.c?

(I use the one from current dev branch)

Note: adb shell input tap ... calls this: https://github.com/aosp-mirror/platform_frameworks_base/blob/22e3e74e4b3b989d20bb17ca7d54f95b67d6c02c/cmds/input/src/com/android/commands/input/Input.java#L221-L226

@GetCurious
Copy link

i got nothing (instead and in-addition).

@rom1v
Copy link
Collaborator

rom1v commented Jul 7, 2020

@GetCurious OK, just to be sure, you are building the server (you don't use the prebuilt-server)?

What we have to understand is why this works (adb shell input tap ...): https://github.com/aosp-mirror/platform_frameworks_base/blob/22e3e74e4b3b989d20bb17ca7d54f95b67d6c02c/cmds/input/src/com/android/commands/input/Input.java#L221-L226

but this does not:

MotionEvent event = MotionEvent
.obtain(lastTouchDown, now, action, pointerCount, pointerProperties, pointerCoords, 0, buttons, 1f, 1f, DEVICE_ID_VIRTUAL, 0,
InputDevice.SOURCE_TOUCHSCREEN, 0);

@GetCurious
Copy link

$ cd scrcpy
$ vim app/src/input_manager.c
$ vim server/src/main/java/com/genymobile/scrcpy/Controller.java
$ meson x --buildtype release --strip -Db_lto=true
$ ninja -Cx 
$ ./run x

anything i miss?

@rom1v
Copy link
Collaborator

rom1v commented Jul 8, 2020

Let's try to use a device id matching the input source, like they do:

diff --git a/server/src/main/java/com/genymobile/scrcpy/Controller.java b/server/src/main/java/com/genymobile/scrcpy/Controller.java
index 71e7ec9c..c1949f36 100644
--- a/server/src/main/java/com/genymobile/scrcpy/Controller.java
+++ b/server/src/main/java/com/genymobile/scrcpy/Controller.java
@@ -195,12 +195,26 @@ public class Controller {
             }
         }
 
+        int deviceId = getInputDeviceId(InputDevice.SOURCE_TOUCHSCREEN);
+        Ln.i("device id = " + deviceId);
         MotionEvent event = MotionEvent
-                .obtain(lastTouchDown, now, action, pointerCount, pointerProperties, pointerCoords, 0, buttons, 1f, 1f, DEVICE_ID_VIRTUAL, 0,
+                .obtain(lastTouchDown, now, action, pointerCount, pointerProperties, pointerCoords, 0, buttons, 1f, 1f, deviceId, 0,
                         InputDevice.SOURCE_TOUCHSCREEN, 0);
         return device.injectEvent(event);
     }
 
+    private static int getInputDeviceId(int inputSource) {
+        final int DEFAULT_DEVICE_ID = 0;
+        int[] devIds = InputDevice.getDeviceIds();
+        for (int devId : devIds) {
+            InputDevice inputDev = InputDevice.getDevice(devId);
+            if (inputDev.supportsSource(inputSource)) {
+                return devId;
+            }
+        }
+        return DEFAULT_DEVICE_ID;
+    }
+
     private boolean injectScroll(Position position, int hScroll, int vScroll) {
         long now = SystemClock.uptimeMillis();
         Point point = device.getPhysicalPoint(position);

@GetCurious
Copy link

no difference

@rom1v
Copy link
Collaborator

rom1v commented Jul 8, 2020

What's the id printed in the console when you click with this change?

@GetCurious
Copy link

how do i do that?

@rom1v
Copy link
Collaborator

rom1v commented Jul 8, 2020

When you execute ./run x, what is the output in the console?

@GetCurious
Copy link

➜  scrcpy git:(dev) ✗ ./run x                                                      
INFO: scrcpy 1.14 <https://github.com/Genymobile/scrcpy>
x/server/scrcpy-server: 1 file pushed, 0 skipped. 107.0 MB/s (33374 bytes in 0.000s)
[server] INFO: Device: OPPO CPH1969 (Android 10)
INFO: Renderer: opengl
INFO: OpenGL version: 4.6.0 NVIDIA 440.100
INFO: Trilinear filtering enabled
INFO: Initial texture: 1080x2336
INFO: New texture: 888x1920

@rom1v
Copy link
Collaborator

rom1v commented Jul 8, 2020

And nothing more, even when you click in the scrcpy window?

EDIT: in itself:

INFO: Initial texture: 1080x2336
INFO: New texture: 888x1920

it's a bit surprising (the device sends frame at a different resolution from what has been requested). But probably not related.

@GetCurious
Copy link

GetCurious commented Jul 8, 2020

that's right... nothing more.
everything except left-click works

@rom1v
Copy link
Collaborator

rom1v commented Aug 19, 2020

@eku
Copy link

eku commented Aug 19, 2020

Yes, I know the FAQ and this point. The setting also exists in my phone but cannot be changed for unknown reason.
Please excuse the disturbance by me.

@Rudrarokaya
Copy link

Rudrarokaya commented Jan 17, 2021

okay, after two hours of trying i finally found out the solution. just run this command in terminal
(sudo apt install adb ffmpeg libsdl2-2.0-0 make gcc pkg-config meson ninja-build libavcodec-dev libavformat-dev libavutil-dev libsdl2-dev) and after that run scrcpy -m1920 in terminal.
enjoy.
#note: this was only tested in ubuntu and oppof9 phone

@Rudrarokaya
Copy link

and for the desktop entry in linux (ubuntu) just create a file under /usr/share/applications/scrcpy.desktop
[Desktop Entry]
Version=1.0
Type=Application
Name=scrcpy
GenericName=scrcpy
Comment=Screen mirroring application
Exec=scrcpy -m1920%F
Icon=phone-symbolic
Terminal=false
X-MultipleArgs=false
Categories=Development;GTK;
StartupNotify=true

@rom1v rom1v mentioned this issue Sep 3, 2021
@Britford
Copy link

In case this helps troubleshoot the display size issues:
tl;dr
scrcpy cuts off part of the screen instead of scaling it. 1200x1920 gets cut down to 1080x1900. -m 1700 fixes it.

My Lenovo tablet has a 1920x1200 screen. scrcpy outputs 1920x1080 and the other 120 pixels are just cut off. In portrait mode, the right side of the screen is cut off. In landscape, the bottom of the screen is missing. Touch events do not work.

scrcpy -m 1920 # does not help
scrcpy -m 1700 # seems to work, entire screen is displayed, touch works
I don't know the exact point, but values over 1700 work, less than 1700 or so do not. 1700 seems like an odd value to fix the issue. Maybe the anomaly will help diagnose the root cause.

scrcpy
scrcpy 1.24 https://github.com/Genymobile/scrcpy
scrcpy-server: 1 file pushed, 0 skipped. 145.9 MB/s (41159 bytes in 0.000s)
[server] INFO: Device: LENOVO Lenovo TB-X606F (Android 10)
INFO: Renderer: direct3d
INFO: Initial texture: 1200x1920
INFO: New texture: 1080x1920
[server] WARN: Ignore touch event, it was generated for a different device size

image

2022-08-30_14h44_26

@hhhaiai
Copy link

hhhaiai commented Mar 15, 2024

i have same case.

scrcpy 1.24 <https://github.com/Genymobile/scrcpy>
/opt/local/share/scrcpy/scrcpy-server:...ped. 46.3 MB/s (41159 bytes in 0.001s)
[server] INFO: Device: OPPO PACM00 (Android 10)
2024-03-15 16:30:17.679 scrcpy[9815:243314] INFO: Renderer: metal
2024-03-15 16:30:17.686 scrcpy[9815:243314] ERROR: Could not open image codec: /opt/local/share/icons/hicolor/256x256/apps/scrcpy.png
2024-03-15 16:30:17.686 scrcpy[9815:243314] WARN: Could not load icon
2024-03-15 16:30:17.686 scrcpy[9815:243314] INFO: Initial texture: 1080x2280
2024-03-15 16:30:18.087 scrcpy[9815:243314] INFO: New texture: 910x1920
[server] WARN: Ignore touch event, it was generated for a different device size
[server] WARN: Ignore touch event, it was generated for a different device size
[server] WARN: Ignore touch event, it was generated for a different device size
[server] WARN: Ignore touch event, it was generated for a different device size

@rom1v
Copy link
Collaborator

rom1v commented Mar 15, 2024

@hhhaiai see #1645.

@rom1v
Copy link
Collaborator

rom1v commented Mar 15, 2024

@hhhaiai And also:

scrcpy 1.24

Ultra-old. You should upgrade to the latest version.

@hhhaiai
Copy link

hhhaiai commented Mar 18, 2024

@hhhaiai And also:

scrcpy 1.24

Ultra-old. You should upgrade to the latest version.

ok

@hhhaiai
Copy link

hhhaiai commented Mar 18, 2024

@hhhaiai see #1645.

3Q

@hhhaiai
Copy link

hhhaiai commented Mar 19, 2024

@rom1v same case.

scrcpy --prefer-text
scrcpy 2.4 <https://github.com/Genymobile/scrcpy>
INFO: ADB device found:
INFO:     -->   (usb)  PVRKKRAAV8IBININ                device  PACM00
/usr/local/Cellar/scrcpy/2.4/share/scrcpy/scrcpy-server: 1 file pushed, 0 skipped. 246.9 MB/s (69007 bytes in 0.000s)
[server] INFO: Device: [OPPO] OPPO PACM00 (Android 10)
[server] WARN: Audio disabled: it is not supported before Android 11
INFO: Renderer: metal
WARN: Demuxer 'audio': stream explicitly disabled by the device
INFO: Texture: 1080x2280
INFO: Texture: 910x1920

[server] WARN: Ignore touch event, it was generated for a different device size
[server] WARN: Ignore touch event, it was generated for a different device size
[server] WARN: Ignore touch event, it was generated for a different device size
[server] WARN: Ignore touch event, it was generated for a different device size
[server] WARN: Ignore touch event, it was generated for a different device size
[server] WARN: Ignore touch event, it was generated for a different device size
[server] WARN: Ignore touch event, it was generated for a different device size
[server] WARN: Ignore touch event, it was generated for a different device size
[server] WARN: Ignore touch event, it was generated for a different device size
[server] WARN: Ignore touch event, it was generated for a different device size

@hhhaiai
Copy link

hhhaiai commented Mar 19, 2024

@hhhaiai And also:

scrcpy 1.24

Ultra-old. You should upgrade to the latest version.

already update 2.4, still has same exception

@rom1v
Copy link
Collaborator

rom1v commented Mar 19, 2024

@hhhaiai #1645

scrcpy -m1920

@hhhaiai
Copy link

hhhaiai commented Mar 22, 2024

scrcpy -m1920

It's ok。 why not auto support general size.

3Q~~

@hhhaiai
Copy link

hhhaiai commented Mar 22, 2024

@hhhaiai #1645

scrcpy -m1920

very 3Q. this play is OK . "scrcpy -m1920" ,the oppo phone size "INFO: Texture: 912x1920"

check size and update

@rom1v
Copy link
Collaborator

rom1v commented Mar 22, 2024

why not auto support general size.

Because this is a bug in the encoder: we request a size, it produces a video with a different size without notice (and we can't know that easily on the device side without parsing the encoded video stream).

In general, scrcpy tries a size, then attempts a smaller size if it fails… But some encoders don't fail, but decide to produce a video with a different size instead.

@hhhaiai
Copy link

hhhaiai commented Mar 22, 2024

why not auto support general size.

Because this is a bug in the encoder: we request a size, it produces a video with a different size without notice (and we can't know that easily on the device side without parsing the encoded video stream).

In general, scrcpy tries a size, then attempts a smaller size if it fails… But some encoders don't fail, but decide to produce a video with a different size instead.

First startup, will prompt [INFO: Texture: 910x1920]
Should we first detect the resolution before the decoder starts working, that is, adjust the default parameters based on the second 1920, and then start the encoder

  • logs
scrcpy 2.4 <https://github.com/Genymobile/scrcpy>
INFO: ADB device found:
INFO:     -->   (usb)  PVRKKRAAV8IBININ                device  PACM00
/usr/local/Cellar/scrcpy/2.4/share/scrcpy/scrcpy-server: 1 file pushed, 0 skipped. 68.0 MB/s (69007 bytes in 0.001s)
[server] INFO: Device: [OPPO] OPPO PACM00 (Android 10)
[server] WARN: Audio disabled: it is not supported before Android 11
INFO: Renderer: metal
WARN: Demuxer 'audio': stream explicitly disabled by the device
INFO: Texture: 1080x2280
INFO: Texture: 910x1920

@rom1v
Copy link
Collaborator

rom1v commented Mar 22, 2024

The actual resolution is detected on the client side, once the video stream is parsed/decoded.

The server side thinks the resolution is what it requested.

When then client sends a mouse events, the server detects a mismatch (which can happen in expected conditions like when the device is physically rotated between the instant an event is triggered on the client side and the time it is injected on the server side), and ignores the event (with a warning).

@hhhaiai
Copy link

hhhaiai commented Mar 22, 2024

The actual resolution is detected on the client side, once the video stream is parsed/decoded.

The server side thinks the resolution is what it requested.

When then client sends a mouse events, the server detects a mismatch (which can happen in expected conditions like when the device is physically rotated between the instant an event is triggered on the client side and the time it is injected on the server side), and ignores the event (with a warning).

How is this solution (scrcpy - m1920 *) implemented?
When the user types the **scrcpy **command, they do not directly start the operation. Instead, they first check the resolution and obtain the truly operable resolution (INFO: Texture: 910x1920). At this time, they work according to the implementation of the scheme (scrcpy - m1920 ).
Equivalent to, after default detection, we input different resolutions to different devices, and scrcpy is approximately equal to the dynamic parsing resolution, and then execute scrcpy - m1920

@rom1v
Copy link
Collaborator

rom1v commented Mar 22, 2024

Instead, they first check the resolution and obtain the truly operable resolution

That's a lot of work / round trip / guess to workaround an encoder bug which impacts only few devices.

Btw, in some cases -m1920 does not fix the problem, because many encoders require both dimensions to be a multiple of 8 (for example see #1645 (comment)). And you would have to handle this encoder bug on rotation too (the encoder might accept the initial size, but after a rotation the width or height exceeds its maximum and sends a video with another size). Anyway, a good solution would be to make the encoder fail if it does not support the requested size, but that's up to the encoders implementations.

Btw, you could try with another encoder.

@hhhaiai
Copy link

hhhaiai commented Mar 22, 2024

Instead, they first check the resolution and obtain the truly operable resolution

That's a lot of work / round trip / guess to workaround an encoder bug which impacts only few devices.

Btw, in some cases -m1920 does not fix the problem, because many encoders require both dimensions to be a multiple of 8 (for example see #1645 (comment)). And you would have to handle this encoder bug on rotation too (the encoder might accept the initial size, but after a rotation the width or height exceeds its maximum and sends a video with another size). Anyway, a good solution would be to make the encoder fail if it does not support the requested size, but that's up to the encoders implementations.

Btw, you could try with another encoder.

What you said is indeed an issue, and compatibility issues with a few devices are indeed a troublesome matter

Now is a compatibility issue with some devices. As time goes on, it is expected that the number of devices with this issue will continue to increase. It may not be - m1920 in the future, but may be devices with a resolution of -m3xxx or higher

Replace the encoder as per your proposal

$scrcpy -- list encoders
Scrcpy 2.4< https://github.com/Genymobile/scrcpy >
INFO: ADB device found:
INFO: -->(USB) PVRKRAAV8IBININ device PACM00
/Usr/local/Cellar/scrcpy/2.4/share/scrcpy/scrcpy server: 1 file pushed, 0 skipped 282.3 MB/s (69007 bytes in 0.000s)
[server] INFO: Device: [OPPO] OPPO PACM00 (Android 10)
[server] INFO: List of video encoders:
--Video code=h264-- video encoder='OMX MTK VIDEO ENCODER AVC '
--Video code=h264-- video encoder='c2. android. avc. encoder '
--Video code=h264-- video encoder='OMX. Google. h264. encoder '
[server] INFO: List of audio encoders:
--Audio code=opus -- audio encoder='c2. android. opus. encoder '
--Audio code=aac -- audio encoder='OMX. Google. aac. encoder '
--Audio code=aac -- audio encoder='c2. android. aac. encoder '
--Audio code=flac -- audio encoder='OMX. Google. flac. encoder '
--Audio code=flac -- audio encoder='c2. android. flac. encoder '

There are two decoders that can work. Can the decoders be dynamically switched during operation

#Success
--Video code=h264-- video encoder='c2. android. avc. encoder '
--Video code=h264-- video encoder='OMX. Google. h264. encoder '

Of course, this is also a suggestion
Thank you from the bottom of my heart for your dedication. This excellent project is really amazing

@rom1v
Copy link
Collaborator

rom1v commented Mar 22, 2024

it is expected that the number of devices with this issue will continue to increase.

Why that? If the encoder does not support the requested size, it should fail (as most of encoders do).

Also, another problem is that the encoder produces a video at a different resolution than that requested, but we don't know how the video is scaled/cropped (some may add some black borders, other may stretch the video…), so we don't know for sure how to map input events back to thebdevice coordinates. Btw, in your case, one dimension is 910, which is not even a multiple of 8.

Can the decoders be dynamically switched during operation

No, one must be explicitly selected. There is no absolute criteria to select "the best".

@hhhaiai
Copy link

hhhaiai commented Mar 23, 2024

it is expected that the number of devices with this issue will continue to increase.

Why that? If the encoder does not support the requested size, it should fail (as most of encoders do).

Also, another problem is that the encoder produces a video at a different resolution than that requested, but we don't know how the video is scaled/cropped (some may add some black borders, other may stretch the video…), so we don't know for sure how to map input events back to thebdevice coordinates. Btw, in your case, one dimension is 910, which is not even a multiple of 8.

Can the decoders be dynamically switched during operation

No, one must be explicitly selected. There is no absolute criteria to select "the best".

ok. 3Q

@riotcult
Copy link

#1518 (comment)

But probably not related.

Oh in fact, that might be related: the client sends clicks for a window having dimensions different from the ones provided by the device.

If you remove these lines, it should "work" (it may click at a wrong location though):

if (!unlockedVideoSize.equals(clientVideoSize)) {
// The client sends a click relative to a video with wrong dimensions,
// the device may have been rotated since the event was generated, so ignore the event
return null;
}

INFO: Initial texture: 1080x2336
INFO: New texture: 888x1920

What if you run scrcpy -m1920? Do clicks work in that case?

Hey man I am new to all this can you guide me through all that I have to do for this to work?

@rom1v
Copy link
Collaborator

rom1v commented Apr 25, 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

9 participants