Kann man binäres stdout von einem adb Shell-Befehl lesen? Zum Beispiel umfassen alle Beispiele für die Verwendung von screencap zwei Schritte:
adb Shell screencap -p /sdcard/foo.png
adb pull /sdcard/foo.png
Der Dienst unterstützt jedoch das Schreiben in stdout. Sie können zum Beispiel Folgendes tun:
adb Shell "screencap -p > /sdcard/foo2.png"
adb pull /sdcard/foo2.png
Und das funktioniert genauso gut. Aber was ist mit dem Lesen der Ausgabe über ADB? Was ich tun möchte, ist folgendes:
adb Shell screencap -p > foo3.png
Und vermeiden Sie das Zwischenschreiben auf die SD-Karte. Dies erzeugt etwas, das aussieht wie eine PNG-Datei (wobei strings foo3.png
etwas mit einem IHDR, IEND usw. generiert) und ungefähr die gleiche Größe hat, aber die Datei ist hinsichtlich Bildlesern beschädigt.
Ich habe auch versucht, dies mit ddmlib in Java zu tun, und die Ergebnisse sind die gleichen. Gerne verwende ich jede benötigte Bibliothek. Mein Ziel ist es, die Gesamtzeit für die Aufnahme zu reduzieren. Bei Verwendung der Zwei-Befehls-Lösung dauert es auf meinem Gerät etwa 3 Sekunden, um das Image abzurufen. Die Verwendung von ddmlib und das Aufnehmen von stdout dauert weniger als 900 ms, funktioniert aber nicht!
Kann man das machen?
BEARBEITEN: Hier ist der Hexdump von zwei Dateien. Das erste, screen.png, stammt von stdout und ist beschädigt. Der zweite, xscreen, stammt von der Zwei-Befehls-Lösung und funktioniert. Die Bilder sollten visuell identisch sein.
$ hexdump -C screen.png | head
00000000 89 50 4e 47 0d 0d 0a 1a 0d 0a 00 00 00 0d 49 48 |.PNG..........IH|
00000010 44 52 00 00 02 d0 00 00 05 00 08 06 00 00 00 6e |DR.............n|
00000020 ce 65 3d 00 00 00 04 73 42 49 54 08 08 08 08 7c |.e=....sBIT....||
00000030 08 64 88 00 00 20 00 49 44 41 54 78 9c ec bd 79 |.d... .IDATx...y|
00000040 9c 1d 55 9d f7 ff 3e 55 75 f7 de b7 74 77 d2 d9 |..U...>Uu...tw..|
00000050 bb b3 27 10 48 42 16 c0 20 01 86 5d 14 04 11 dc |..'.HB.. ..]....|
00000060 78 44 9d c7 d1 d1 11 78 70 7e 23 33 8e 1b 38 33 |xD.....xp~#3..83|
00000070 ea 2c 8c 8e 0d 0a 08 a8 23 2a 0e 10 82 ac c1 40 |.,......#*[email protected]|
00000080 12 02 81 24 64 ef ec 5b ef fb 5d 6b 3b bf 3f ea |...$d..[..]k;.?.|
00000090 de db dd 49 27 e9 ee 74 77 3a e3 79 bf 5e 37 e7 |...I'..tw:.y.^7.|
$ hexdump -C xscreen.png | head
00000000 89 50 4e 47 0d 0a 1a 0a 00 00 00 0d 49 48 44 52 |.PNG........IHDR|
00000010 00 00 02 d0 00 00 05 00 08 06 00 00 00 6e ce 65 |.............n.e|
00000020 3d 00 00 00 04 73 42 49 54 08 08 08 08 7c 08 64 |=....sBIT....|.d|
00000030 88 00 00 20 00 49 44 41 54 78 9c ec 9d 77 98 1c |... .IDATx...w..|
00000040 c5 99 ff 3f d5 dd 93 37 27 69 57 5a e5 55 4e 08 |...?...7'iWZ.UN.|
00000050 24 a1 00 58 18 04 26 08 8c 01 83 31 38 c0 19 9f |$..X..&....18...|
00000060 ef 7c c6 3e 1f 70 f8 7e 67 ee 71 e2 b0 ef ce f6 |.|.>.p.~g.q.....|
00000070 f9 ec 73 04 1b 1c 31 60 23 84 30 22 88 a0 40 10 |..s...1`#.0"[email protected]|
00000080 08 65 69 95 d3 4a 9b c3 c4 4e f5 fb a3 67 66 77 |.ei..J...N...gfw|
00000090 a5 95 b4 bb da a4 73 7d 9e 67 55 f3 ed 50 5d dd |......s}.gU..P].|
Auf den ersten Blick scheint es, als würden ein paar zusätzliche 0x0d (13) Bytes hinzugefügt. Wagenrückkehr ?? Läutet das Glocken? Mischt es in einigen leeren Zeilen?
Es tut mir leid, auf eine alte Frage eine Antwort zu posten, aber ich bin selbst auf dieses Problem gestoßen und wollte es nur über die Shell tun. Das hat gut für mich funktioniert:
adb Shell screencap -p | sed 's/^M$//' > screenshot.png
Das ^M
ist ein Zeichen, das ich durch Drücken von Strg + V -> Strg + M erhalten habe.
adb Shell screencap -p | sed 's/\r$//' > screenshot.png
hat den Trick auch für mich gemacht.
Im Gegensatz zu adb Shell
verwendet der adb exec-out
-Befehl keine pty
, wodurch die binäre Ausgabe beeinträchtigt wird. Das kannst du tun
adb exec-out screencap -p > test.png
https://Android.googlesource.com/platform/system/core/+/5d9d434efadf1c535c7fea634d5306e18c68ef1f
Wenn Sie diese Technik für einen Befehl verwenden, der eine Ausgabe in STDERR erzeugt, sollten Sie sie nach /dev/null
umleiten. Andernfalls wird adb
STDERR in seinen STDOUT einschließen, wodurch Ihre Ausgabe beschädigt wird. Wenn Sie beispielsweise versuchen, ein Verzeichnis zu sichern und zu komprimieren:
adb exec-out "tar -zcf - /system 2>/dev/null" > system.tar.gz
Wie bereits erwähnt, führt "adb Shell" eine Konvertierung von Linefeed (0x0a) in Wagenrücklauf + Linefeed (0x0d 0x0a) durch. Dies wird von der Pseudo-tty-Zeilendisziplin durchgeführt. Da der Shell kein "stty" -Befehl zur Verfügung steht, gibt es keine einfache Möglichkeit, sich mit den Terminaleinstellungen auseinanderzusetzen.
Es ist möglich, mit ddmlib das zu tun, was Sie wollen. Sie müssen Code schreiben, der Befehle auf dem Gerät ausführt, die Ausgabe erfasst und über das Kabel sendet. Dies ist mehr oder weniger das, was DDMS für bestimmte Funktionen tut. Dies kann mehr Ärger als sein Wert sein.
Die repair()
-Lösung, bei der alle CRLF in LF konvertiert werden, fühlt sich wackelig an, ist jedoch tatsächlich zuverlässig, da die "beschädigte" Konvertierung von LF in CRLF deterministisch ist. Ich habe das Gleiche getan, um versehentliche FTP-Übertragungen im ASCII-Modus zu reparieren.
Es ist erwähnenswert, dass das PNG-Dateiformat explizit darauf ausgelegt ist, genau diese (und verwandten) Probleme zu erfassen. Die magische Zahl beginnt mit 0x89, um alles abzufangen, das hohe Bits entfernt, gefolgt von "PNG", sodass Sie leicht erkennen können, was sich in der Datei befindet, gefolgt von CR LF, um verschiedene ASCII -Zeilenkonverter abzufangen, dann 0x1a alte MS-DOS-Programme, die Ctrl-Z als spezielle Dateiende-Markierung verwendet haben, und dann einen einsamen LF. Wenn Sie sich die ersten Bytes der Datei ansehen, können Sie genau erkennen, was mit der Datei gemacht wurde.
... was bedeutet, dass Ihre repair()
-Funktion sowohl "beschädigte" als auch "reine" Eingaben akzeptieren und zuverlässig feststellen kann, ob sie etwas tun muss.
Edit: Eine weitere Anmerkung: Es ist möglich, dass die geräteseitige Binärdatei das tty konfiguriert, um die Konvertierung mit cfmakeraw()
zu vermeiden. Siehe prepareRawOutput()
im Befehl screenrecord in Android 5.0, mit dem Rohvideos von der Live-Bildschirmaufnahme über die ADB-Shell-Verbindung gesendet werden können.
Nach tieferem Eintauchen in die Hex-Dumps wurde klar, dass die Shell bei jeder Ausgabe des Zeichens 0x0A 0x0D 0x0A emittierte. Ich habe den Stream mit dem folgenden Code repariert und nun sind die Binärdaten korrekt. Nun stellt sich natürlich die Frage, warum adb Shell das tut. In jedem Fall wird dadurch das Problem behoben.
static byte[] repair(byte[] encoded) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
for (int i=0; i<encoded.length; i++) {
if (encoded.length > i+1 && encoded[i] == 0x0d && encoded[i+1] == 0x0a) {
baos.write(0x0a);
i++;
} else {
baos.write(encoded[i]);
}
}
try {
baos.close();
} catch (IOException ioe) {
}
return baos.toByteArray();
}
EDIT: Mir wurde klar, warum das so ist. Es konvertiert LF in CR/LF wie alte DOS. Ich frage mich, ob es irgendwo eine Einstellung gibt, um das auszuschalten?
Die beste Lösung ist, den adb exec-out
-Befehl wie @AjeetKhadke vorgeschlagen zu verwenden.
Lassen Sie mich den Unterschied zwischen der Ausgabe von adb Shell
und adb exec-out
erläutern:
~$ adb Shell "echo -n '\x0a'" | xxd -g1
00000000: 0d 0a
~$ adb exec-out "echo -n '\x0a'" | xxd -g1
00000000: 0a
Es funktioniert auch unter Windows (ich verwende hexdump
aus GNUWin32 Hextools für die Demo):
C:\>adb Shell "echo -n '\x0a'" | hexdump
00000000: 0D 0A
C:\>adb exec-out "echo -n '\x0a'" | hexdump
00000000: 0A
Der Nachteil ist, dass das Gerät und der Host-PC das adb exec-out
V2-Protokoll unterstützen müssen, um von der Verwendung des adb Shell
-Befehls profitieren zu können.
Es ist ziemlich trivial, auf die PC-Seite zu achten - aktualisieren Sie einfach das platform-tools
-Paket (das die adb
-Binärdatei enthält) auf die neueste Version. Die Version des Daemon adbd
auf dem Gerät ist mit der Android-Version verknüpft. Das adb Shell
V2-Protokoll wurde in Android 5.0 zusammen mit der vollständigen adb
-Überholung (von c
bis C++
-Code) eingeführt. Es gab jedoch einige Regressionen (sog. Bugs), so dass die adb exec-out
-Funktionalität in Android 5.x noch begrenzt war. Und schließlich gibt es keine Unterstützung für Android 4.x und ältere Geräte. Glücklicherweise sinkt der Anteil dieser älteren Geräte, die noch für die Entwicklung verwendet werden, rapide.
Ja, unter Unix/Linux/Mac OS X können Sie die binäre Ausgabe von adb Shell erhalten, indem Sie "stty -onlcr;" zu deinem Befehl ( NEIN~~ müssen ein verwurzeltes Android sein).
1.Laden Sie die ausführbare Datei "stty" herunter.
http://www.busybox.net/downloads/binaries/latest/
Verwenden Sie für altes Android busybox-armv5l, andere verwenden busybox-armv7l.
Datei umbenennen in "stty"
2.Uploda-Datei "stty" auf Android und die entsprechende Berechtigung festlegen.
adb Push somelocaldir/stty /data/local/tmp/
adb Shell chmod 777 /data/local/tmp/stty
3."Stty -onlcr" voranstellen zu deinem Befehl so;
adb Shell /data/local/tmp/stty -onlcr\; screencap -p > somelocaldir/output.png
or:
adb Shell "/data/local/tmp/stty -onlcr; screencap -p" > somelocaldir/output.png
or (Only for Windows):
adb Shell /data/local/tmp/stty -onlcr; screencap -p > somelocaldir/output.png
Erledigt!
Unter Windows wird jedoch standardmäßig LF von Android in CR CR LF konvertiert.
Selbst wenn Sie den obigen Schritt ausgeführt haben, erhalten Sie immer noch CR LF.
Dies scheint, weil local adb.exe fwrite verwendet, wodurch CR vorangestellt wird.
Ich habe keine andere Möglichkeit dazu, CR LF in LF manuell unter Windows-Betriebssystemen zu konvertieren.
Ein anderer Weg:
adb Shell "busybox stty raw; screencap -p "> foo3.png
ABER, wie @ osexp2003 sagte, das funktioniert nicht unter Windows.
Hier ist eine Lösung, die überall funktioniert (einschließlich Linux und Windows).
Sie benötigen das Dienstprogramm netcat
, das häufig als nc
bezeichnet wird.
Wenn sowohl nc
als auch busybox nc
Auf Ihrem Gerät fehlschlagen, benötigen Sie busybox
. Sie können entweder den busybox-Installer von Play Market (root erforderlich) verwenden oder Lösung von osexp20 (laden Sie die busybox von offizielle Seite herunter, setzen Sie sie auf /data/local/tmp/
Gerät hinzufügen und Ausführungsberechtigung hinzufügen).
Die Idee ist, netcat
als primitiven HTTP-Server zu verwenden.
Nun, nicht einmal ein richtiger Server. Es wird nur seine Eingabe als Antwort auf any TCP Verbindung (sei es HTTP-Anfrage vom Browser, Telnet-Verbindung oder nur netcat
) und kündigen.
Führen Sie den Befehl, von dem Sie eine Ausgabe erhalten möchten, folgendermaßen aus:
adb Shell 'screencap -p | busybox nc -p 8080 -l >/dev/null'
Im obigen Beispiel erstellt screencap -p
Einen Screenshot (PNG-Bild) und leitet ihn an netcat
weiter.-l
Weist netcat
an, als Server zu fungieren (auf Verbindung zu warten), und -p 8080
Weist es an, TCP zu verwenden Port 8080. Wenn Sie >/dev/null
weglassen, wird einfach eine eingehende HTTP-GET-Anfrage an Ihr Terminal gesendet.
Das obige Beispiel wartet darauf, dass jemand eine Verbindung herstellt, einen Screenshot sendet und erst dann beendet.
Natürlich können Sie es auch ohne adb Shell
Ausführen, z. vom Terminal-Emulator auf Ihrem Gerät.
Nachdem Sie Ihren Befehl wie oben ausgeführt haben , können Sie die Ausgabe von Ihrem Telefon herunterladen, indem Sie http://ip.of.your.phone:8080
im Browser oder auf andere Weise, zum Beispiel mit netcat
:
busybox nc ip.of.your.phone:8080 >screenshot.png
Wenn Sie ein USB-Kabel zum Herunterladen verwenden möchten , müssen Sie die Verbindung mit ADB wie folgt weiterleiten:
adb forward tcp:7080 tcp:8080
Danach können Sie localhost:7080
Anstelle von ip.of.your.phone:8080
Verwenden.
Sie können diese Weiterleitung mit folgendem Befehl entfernen:
adb forward --remove tcp:7080
versuche diese Jungs:
adb Shell screencap -p | Perl -pe 's/\x0D\x0A/\x0A/g' > screen.png
Es ist auch möglich, base64 dafür zu verwenden, also kodiere es einfach mit:
base64 foo3.png>foo3.png.base64
und dann unter Windows mit einem base64-Dienstprogramm oder möglicherweise notepad ++, um die Datei zu entschlüsseln.
Oder in Linux/Cygwin:
base64 -d foo3.png.base64>foo3.png
Ich stelle die Methode zur Verwendung von Python-Get-Image-Bytes mit Adb hier. Vielleicht ist dies für jemanden hilfreich, der auf dieses Problem gestoßen ist. Der Code lautet wie folgt:
pipe = subprocess.Popen("adb Shell screencap -p",
stdin=subprocess.PIPE,
stdout=subprocess.PIPE, Shell=True)
image_bytes = pipe.stdout.read().replace(b'\r\n', b'\n')
gray_image = cv2.imdecode(np.fromstring(image_bytes, np.uint8), cv2.IMREAD_GRAYSCALE)
Sie können auch den Standardbefehl dos2unix
verwenden, sofern verfügbar.
(apt-get install dos2unix
wenn Sie Debian/Ubuntu verwenden. Es gibt wahrscheinlich Builds für Windows, OS X usw., wenn Sie googeln.
dos2unix
konvertiert CRLF in LF genauso wie die Funktion repair()
von Eric Lange.
adb Shell screencap -p | dos2unix -f > screenshot.png
oder reparieren Sie eine beschädigte Datei (direkt):
dos2unix -f screenshot.png
Sie benötigen den -f
, um die Verarbeitung von Binärdateien zu erzwingen.