Genymobile / scrcpy

Display and control your Android device
Apache License 2.0
111.58k stars 10.69k forks source link

Make scrcpy work with adb over tcpip #5

Closed h43z closed 6 years ago

h43z commented 6 years ago

Great useful tool. Thank you. It would be really cool if the device did not have to be connected via usb.

rom1v commented 6 years ago

Yes, I planned to work on this.

Here are the technical details: https://news.ycombinator.com/item?id=16546276

Actually, it won't work with the current version because of an adb bug ("adb reverse" does not work over "adb connect"): https://issuetracker.google.com/issues/37066218

The solution would be to change the direction of the connection, and use "adb forward", but then scrcpy would try to connect before the server is started, so it would need to retry until connected (and the error would not be noticed on connect, but on first read, due to the tunnel).

Yeradon commented 6 years ago

I just used it with adb connect. It worked fine.

rom1v commented 6 years ago

@Yeradon Hmm… It does not work for me:

$ adb devices
List of devices attached
192.168.0.42:5555   device
$ scrcpy
/usr/local/share/scrcpy/scrcpy-server.jar: 1 file pushed. 0.5 MB/s (19062 bytes in 0.037s)
error: more than one device/emulator
ERROR: "adb reverse" returned with value 1
Yeradon commented 6 years ago

I was using wireless hotspot from my phone to setup the network. Maybe that makes a difference?

h43z commented 6 years ago

@Yeradon Created a hotspot with phone and connected computer to it. Then adb connected and run scrcpy, still same error as above.

jmgao commented 6 years ago

Oops, sorry, that bug languished partly because it was assigned to the wrong person. Your analysis in comment 6 seems correct, so a fix won't be helpful to your users for a while...

However, you might be able to sidestep that by communicating with your device-side process directly via stdin/stdout, instead of having it connect to a forwarded port. It should have identical throughput and start up faster, too.

rom1v commented 6 years ago

Thank you for your investigations ;-)

Your analysis in comment 6 seems correct, so a fix won't be helpful to your users for a while...

:disappointed:

you might be able to sidestep that by communicating with your device-side process directly via stdin/stdout, instead of having it connect to a forwarded port.

In my first PoC, I used stdout to stream the video.

However:

This is for the device-to-computer communication.

But in both direction, how do you do that? adb is not pipe-friendly like ssh. Did I miss something?

jmgao commented 6 years ago

IIRC, not all devices support adb exec-out,

Looks like it was added around Lollipop (API level 20), which is earlier than your minimum supporter version: https://android.googlesource.com/platform/system/core/+/lollipop-release/adb/services.c#426

adb shell output is corrupted depending on the platform and the version (it sometimes replaces \n by \r\n or \r\r\n),

Yeah, this is caused by the device-side adb daemon allocating a TTY instead of a pipe for stdout. exec-in/exec-out solve that by creating a pipe, but they're unidirectional [1] because the adb protocol didn't specify a way to half close a socket, so you'd end up hanging forever. There's a new shell protocol that was added in N that fixes that by adding another protocol on top that allows each side to tell the other that their stdin is closed.

If you're willing to limit yourself to N+, adb shell should Just Work. Alternatively, if you're willing to use JNI, or upload a separate binary, you should be able to set your PTY to raw mode (via cfmakeraw), which will keep the kernel from munging your binary output.

(Or if you're willing to do dirty hacks, you could do something terrible like base64 encoding your output)

if the server starts, it prints its output on stdout, breaking the whole stream (we can minimize the probability by calling adb start-server just before)…

A sufficiently new version of the adb client shouldn't be subject to this (patch that fixed this is https://android.googlesource.com/platform/system/core/+/1fc8f6e0cfdfd5c9dbbd82116fba2ebec82b4659)

But in both direction, how do you do that? adb is not pipe-friendly like ssh. Did I miss something?

Isn't it? There's problems with detecting when your local stdin closes, but AFAIK, this should work even on ancient devices. I dug up the oldest device I could find roughly available, and it seems to be working as expected (i.e. broken, because the terminal isn't raw mode, so it's mirroring the input and then repeating it via cat).

[1] There's no inherent limitation to keep you from using exec bidirectionally, the command line client just does it to keep people from doing things that will just hang. I'm not suggesting that you do it, but you could connect to the adb server yourself and exec: yourself, and clean up after yourself by doing something like printing your pid at the start, and then doing a separate shell invocation to kill that pid at the end.

rom1v commented 6 years ago

Wow, thank you for all this relevant information :+1:

I'm not suggesting that you do it, but you could connect to the adb server yourself and exec: yourself

IIUC, in that case, if the device is unplugged while running, the server keeps running?

rom1v commented 6 years ago

I implemented what I explained in my comment, please checkout the forward branch and tell me whether it works for you.

  1. connect your device to the same wifi as your computer
  2. get the IP of your device (available in Settings → About phone → Status, or adb shell netcfg, or adb shell ip a)
  3. adb tcpip 5555
  4. adb connect DEVICE_IP:5555 (replace DEVICE_IP by your device address)
  5. Unplug your device
  6. scrcpy -b 4M (default is 8Mbps, it may be too high for your wifi connection)

To switch back your device to USB mode: adb usb.

Note that while it now works over tcpip, this is not an optimal solution for streaming a video wirelessly, since the raw stream is still sent over TCP, where a packet loss is very bad for latency, due to head-of-line blocking.

As expected, it is better when the device is plugged. :slightly_smiling_face:

If everything is ok, I'll probably publish a new release soon :wink:

h43z commented 6 years ago

@rom1v awesome! it works for me. It's quite laggy but I guess that was expected.

rom1v commented 6 years ago

It's quite laggy but I guess that was expected.

Depending on the use case, I think that reducing the bitrate and resolution may be a good compromise:

scrcpy --bit-rate 2M --max-size 800
jmgao commented 6 years ago

IIUC, in that case, if the device is unplugged while running, the server keeps running?

I believe this should work (but haven't verified it on an old device). Unplugging the device/ctrl-c should close everything; the problem stems from not being able to report EOF of your write end, and continue waiting for output from the other side. (e.g. the exec moral equivalent of echo foo | adb shell "cat; sleep 1; echo foo" will have cat hang forever, because its stdin won't get EOF).

fei-ke commented 6 years ago

forward working perfectly here: MI 5s with Lineage 15 and Samsung Note8 with stock rom.

unixfox commented 6 years ago

@rom1v I get the same error as you:

/usr/share/scrcpy/scrcpy-server.jar: 1 file pushed. 0.7 MB/s (19038 bytes in 0.025s)
error: more than one device/emulator
ERROR: "adb reverse" returned with value 1
WARN: 'adb reverse' failed, fallback to 'adb forward'
ERROR: Exception on thread Thread[main,5,main]
java.io.IOException: Connection refused
    at android.net.LocalSocketImpl.connectLocal(Native Method)
    at android.net.LocalSocketImpl.connect(LocalSocketImpl.java:292)
    at android.net.LocalSocket.connect(LocalSocket.java:131)
    at com.genymobile.scrcpy.DesktopConnection.connect(DesktopConnection.java:32)
    at com.genymobile.scrcpy.DesktopConnection.open(DesktopConnection.java:37)
    at com.genymobile.scrcpy.Server.scrcpy(Server.java:13)
    at com.genymobile.scrcpy.Server.main(Server.java:70)
    at com.android.internal.os.RuntimeInit.nativeFinishInit(Native Method)
    at com.android.internal.os.RuntimeInit.main(RuntimeInit.java:316)

I don't know what's causing this issue because I followed your instructions: https://github.com/Genymobile/scrcpy/issues/5#issuecomment-372286505

rom1v commented 6 years ago

@unixfox, Did you compile the forward branch using the prebuilt server from v1.0?

unixfox commented 6 years ago

@rom1v Yes I did. I made some research about this issue and I found that adb reverse requires to set the IP and the port in the Developer menu: https://github.com/Microsoft/vscode-react-native/issues/324#issuecomment-284710255. But unfortunately I don't have this option on my Samsung galaxy S7.

rom1v commented 6 years ago

@rom1v Yes I did.

That's the problem: you need the changes for the server too.

I found that adb reverse requires to set the IP and the port in the Developer menu

No, it seems that's in their own app.

rom1v commented 6 years ago

We just released v1.1 including this fix: Scrcpy now works wirelessly.

ghost commented 6 years ago

Not working for me. No issue when used with USB.

a@a-HP-EliteBook:~ adb tcpip 5555

a@a-HP-EliteBook:~ scrcpy -b2M -m800 /usr/local/share/scrcpy/scrcpy-server....shed. 0.1 MB/s (19334 bytes in 0.128s) error: more than one device/emulator ERROR: "adb reverse" returned with value 1 WARN: 'adb reverse' failed, fallback to 'adb forward' a@a-HP-EliteBook:~

Logcat

03-16 11:38:53.080 10728 10728 D AndroidRuntime: >>>>>> START com.android.internal.os.RuntimeInit uid 2000 <<<<<< 03-16 11:38:53.086 10728 10728 D AndroidRuntime: CheckJNI is OFF

rom1v commented 6 years ago

Can you adb shell once connected via adb connect?

ghost commented 6 years ago

Using lineage 14.1. Tried on 3 devices using lineage OS. Same result a@a-HP-EliteBook:~ adb shell tomato:/ $

rom1v commented 6 years ago

OK. Could you try in debug mode, to get more logs, please?

Just:

meson builddir # without additional parameters
ninja -C builddir
./run builddir
ghost commented 6 years ago

After entering ninja -C builddir, there was no progress. I had to interrupt it . I encountered it even when I first built with parameters and so I used build commands with prebuilt server and then build was ok.

a@a-HP-EliteBook:~/scrcpy-master$ ninja -C builddir
ninja: Entering directory `builddir'
[30/30] Linking target app/scrcpy.^C
ninja: build stopped: interrupted by user.

I put the prebuilt server in builddir/server folder and proceeded with ./run buildir

a@a-HP-EliteBook:~ /scrcpy-master ./run builddir
builddir/server/scrcpy-server.jar: 1 file pushed. 0.5 MB/s (19334 bytes in 0.035s)
error: more than one device/emulator
ERROR: "adb reverse" returned with value 1
WARN: 'adb reverse' failed, fallback to 'adb forward'
DEBUG: Remaining connection attempts: 10
DEBUG: Remaining connection attempts: 9
DEBUG: Remaining connection attempts: 8
DEBUG: Remaining connection attempts: 7
DEBUG: Remaining connection attempts: 6
DEBUG: Remaining connection attempts: 5
DEBUG: Remaining connection attempts: 4
DEBUG: Remaining connection attempts: 3
DEBUG: Remaining connection attempts: 2
DEBUG: Remaining connection attempts: 1
DEBUG: Server terminated
a@a-HP-EliteBook:~ /scrcpy-master
rom1v commented 6 years ago

Just to be sure: is it server v1.1?

ghost commented 6 years ago

Yes

rom1v commented 6 years ago

It would surprise me, but maybe the server takes too much time to start.

Could you check with more connection attempts, please:

--- a/app/src/server.c
+++ b/app/src/server.c
@@ -199,7 +199,7 @@ socket_t server_connect_to(struct server *server, Uint32 timeout_ms) {
     if (!server->tunnel_forward) {
         server->device_socket = net_accept(server->server_socket);
     } else {
-        Uint32 attempts = 10;
+        Uint32 attempts = 100;
         Uint32 delay = 100; // ms
         server->device_socket = connect_to_server(server->local_port, attempts, delay);
     }
rom1v commented 6 years ago

I put the prebuilt server in builddir/server

You should not put it there manually.

Instead, configure with:

meson x … -Dprebuilt_server=/some/external/path/to/scrcpy-server-v1.1.jar

Otherwise, it will be overridden by the call to ninja. (it's ok if you do this just before ./run though)

ghost commented 6 years ago

Increasing connection attempts worked. Thank you very much.

rom1v commented 6 years ago

Increasing connection attempts worked.

Could you tell me how many attempts it required to work, please?

ghost commented 6 years ago
a@a-HP-EliteBook: ~/scrcpy-master$ ./run builddir
builddir/server/scrcpy-server.jar: 1 file pushed. 0.5 MB/s (19334 bytes in 0.037s)
error: more than one device/emulator
ERROR: "adb reverse" returned with value 1
WARN: 'adb reverse' failed, fallback to 'adb forward'
DEBUG: Remaining connection attempts: 100
DEBUG: Remaining connection attempts: 99
DEBUG: Remaining connection attempts: 98
DEBUG: Remaining connection attempts: 97
DEBUG: Remaining connection attempts: 96
DEBUG: Remaining connection attempts: 95
DEBUG: Remaining connection attempts: 94
DEBUG: Remaining connection attempts: 93
DEBUG: Remaining connection attempts: 92
DEBUG: Remaining connection attempts: 91
DEBUG: Remaining connection attempts: 90
DEBUG: Remaining connection attempts: 89
DEBUG: Remaining connection attempts: 88
DEBUG: Remaining connection attempts: 87
DEBUG: Remaining connection attempts: 86
DEBUG: Remaining connection attempts: 85
DEBUG: Remaining connection attempts: 84
DEBUG: Remaining connection attempts: 83
DEBUG: Remaining connection attempts: 82
DEBUG: Remaining connection attempts: 81
DEBUG: Remaining connection attempts: 80
DEBUG: Remaining connection attempts: 79
DEBUG: Remaining connection attempts: 78
DEBUG: Remaining connection attempts: 77
DEBUG: Remaining connection attempts: 76
DEBUG: Remaining connection attempts: 75
DEBUG: Remaining connection attempts: 74
DEBUG: Remaining connection attempts: 73
DEBUG: Remaining connection attempts: 72
DEBUG: Remaining connection attempts: 71
DEBUG: Starting decoder thread
DEBUG: Starting controller thread
INFO: OpenGL shaders: ENABLED
INFO: Created renderer: opengl
INFO: Initial texture: 720x1280
rom1v commented 6 years ago

Wow, so it takes almost 3 seconds for the server socket to listen, that's very slow!

rom1v commented 6 years ago

I increased the number of connection attempts to 50 on master.

felipebueno commented 6 years ago

Hey, it's working for me now as well \o/

Thanks @las2mile for reporting that and @rom1v for this awesome project!

BlueHatHkr commented 6 years ago

I'm having what seems to be a similar issue to las2mile, but on Windows; the adb connection is working in scrcpy version 1.1, yet nothing comes up when I run scrcpy.exe . Any ideas on what I should try updating?

rom1v commented 6 years ago

@Tijimonster Compile from the current master branch, it fixes the adb connection over wifi when it takes too long time to connect.

BlueHatHkr commented 6 years ago

Sorry to bother you again, but I'm having issues with the MSYS compiler and can't get the master branch to compile. Is there anywhere I could get a precompiled master that I could run?

Thanks!

rom1v commented 6 years ago

The adb reverse bug seems to be fixed in AOSP: https://android-review.googlesource.com/c/platform/system/core/+/664644 https://android.googlesource.com/platform/system/core/+/44899eeb53eb6260c20891723709ab38ed246349

:clap:

aeneas01 commented 6 years ago

first, i'm stunned by how impressive scrcpy is, how impressively it works, especially after clunking around with the premium version of vysor for the last year or so... anyway, usb works fine for me but i can't get wifi to work, nothing seems to happen when i try to launch scycpy.exe after the adb wifi steps.. i'm able to get into the shell after connecting tho (adb shell > return: shell@gvwifiue:/ $), not sure where to go from there....

could it be that my android build is too old? i'm using a rather dated galaxy view 18.4 which doesn't seem to be upgradable... the debug bridge version is 1.0.32....

EDIT -

googled the issue and came across:

Confirm that your host computer is connected to the target device: $ adb devices List of devices attached device_ip_address:5555 device

https://developer.android.com/studio/command-line/adb

so i tried and got this:

1|shell@gvwifiue:/ $ adb devices

shell@gvwifiue:/ $

where do i go from here?

mike-pt commented 6 years ago

I'm running 1.2 and still get the error:

$ scrcpy 
/usr/local/Cellar/scrcpy/1.2_1/share/scrcpy/scrcpy-server.jar: 1 file pushed. 3.0 MB/s (19342 bytes in 0.006s)
error: more than one device/emulator
2018-07-09 12:54:38.157 scrcpy[25723:235278] ERROR: "adb reverse" returned with value 1
2018-07-09 12:54:38.157 scrcpy[25723:235278] WARN: 'adb reverse' failed, fallback to 'adb forward'
$ 
rom1v commented 6 years ago

@mike-pt Could you compile in debug mode to get more details, please?

BoredOutOfMyGit commented 6 years ago

Thanks for the heads up on the adb reverse bug @rom1v

I went ahead and confirmed that it does indeed solve this issue with scrcpy!

https://gerrit.dirtyunicorns.com/#/q/status:open+branch:o8x+topic:scrcpy-fix

moderatoburrito commented 6 years ago

Doesn't work for me. The tcpip step returns "invalid port". The IP address is correct. Android device and laptop are connected to the same computer.

rom1v commented 6 years ago

The tcpip step returns "invalid port".

Which command exactly?

moderatoburrito commented 6 years ago

Thanks for the quick response. The command is:

adb tcpip 192.168.0.109

where "192.168.0.109" is my device's IP address.

rom1v commented 6 years ago

OK, the command is:

adb tcpip <port>

It switches the adb mode from USB to TCP on a specific port.

Then you can connect with:

adb connect 192.168.0.109:5555

(assuming port is 5555)

https://www.genymotion.com/blog/open-source-project-scrcpy-now-works-wirelessly/

moderatoburrito commented 6 years ago

Thanks! Progress but still a snag.

Burritos-MacBook:~ burrito$ adb tcpip 5555
restarting in TCP mode port: 5555
Burritos-MacBook:~ burrito$ adb connect 192.168.0.109:5555
connected to 192.168.0.109:5555
Burritos-MacBook:~ burrito$ scrcpy -b 4M
adb: error: failed to get feature set: more than one device/emulator
2018-10-12 11:37:14.149 scrcpy[87631:30959773] ERROR: "adb push" returned with value 1
Burritos-MacBook:~ burrito$ scrcpy -b 4M
/usr/local/Cellar/scrcpy/1.3/share/scr...shed. 0.0 MB/s (18570 bytes in 0.376s)
error: closed
2018-10-12 11:37:30.380 scrcpy[87639:30961410] ERROR: "adb reverse" returned with value 1
2018-10-12 11:37:30.380 scrcpy[87639:30961410] WARN: 'adb reverse' failed, fallback to 'adb forward'
Aborted

I had forgotten at first to unplug the device but it failed even after trying after unplugging.

rom1v commented 6 years ago

Does it work correctly over usb?

moderatoburrito commented 6 years ago

Nope, the direction on the main page did not work, I received:

Burritos-MacBook:~ burrito$ scrcpy -b 2M
/usr/local/Cellar/scrcpy/1.3/share/scr...shed. 1.4 MB/s (18570 bytes in 0.013s)
error: closed
2018-10-12 11:43:50.619 scrcpy[87839:31014847] ERROR: "adb reverse" returned with value 1
2018-10-12 11:43:50.619 scrcpy[87839:31014847] WARN: 'adb reverse' failed, fallback to 'adb forward'
Aborted

This is what brought me to this discussion, to find a solution.

rom1v commented 6 years ago

@moderatoburrito Is your Android >= 5? https://github.com/Genymobile/scrcpy/issues/277