Open toddsierens opened 5 years ago
does ascreenshot not accomplish what you need?
Is there a way to only attain one frame or a screenshot at the time of a request?
It requires some investigations to do it in Java without an Android context (I have no time to do this now).
Sometimes, the implementation of an existing command may help:
adb exec-out screencap -p > file.png
But in this case, it's implemented in C++: https://github.com/aosp-mirror/platform_frameworks_base/blob/master/cmds/screencap/screencap.cpp
does ascreenshot not accomplish what you need?
This is what I am wanting, I am not sure what you are suggesting.
adb exec-out screencap -p > file.png
This is similar to what I was already using, but with the stdout captured by the process and handled without writing to disk. This method, however does take about 1-2 seconds to work.
I did find a method that allows me to access a screenshot in about 0.07s. Which is much better, giving me about 14 fps maximum. Not ideal, but much better than using the screencap shell command.
SurfaceControl.screenshot
method gives a HARDWARE.Bitmap
, which unfortunately must be copied before it is read. Once copied, it can be converted into a bytebuffer and passed along the socket.
This is what I am wanting, I am not sure what you are suggesting.
Sorry, I thought I was in the wrong repo. I know that https://github.com/IntergalacticPenguin/mobile-toolkit has a feature to take screenshots that works pretty well for me. But I believe they are using screencap under the hood, so if that does not work for your use case then it may not work for you.
I looked into this a little bit more, and found that SurfaceControl
invokes a native method, and this native method essentially executes "/system/bin/screencap"
. Now I wanted to see if a java program executing "/system/bin/screencap"
as a subprocess would give any speed-up.
Something of this sort:
try{
Process process = Runtime.getRuntime().exec("/system/bin/screencap");
BufferedReader ob = new BufferedReader(
new InputStreamReader(process.getInputStream()));
int currentChar;
while (( currentChar = ob.read()) != -1) {
buffer.putChar((char) currentChar);
}
} catch(Exception e){
System.out.println(e);
}
But this is about as fast as calling adb shell screencap
(probably is the exact same thing)
This script takes 50 seconds to take 100 screencaps.
I am wondering, how is Surfacecontrol.java
able to execute native code so much faster than Runtime.exec
?
Am I missing something fundamental?
@toddsierens
SurfaceControl.screenshot method gives a HARDWARE.Bitmap, which unfortunately must be copied before it is read. Once copied, it can be converted into a bytebuffer and passed along the socket.
As you mentioned above, that's exactly what I've implemented in DroidCast.
This script takes 50 seconds to take 100 screencaps. ... how is Surfacecontrol.java able to execute native code so much faster than Runtime.exec?
IMO, the reason behind this is IPC (via Android binder) seems more efficient than creating new Process and then getting the job done.
One good reason to add this feature directly to scrcpy is that some applications block taking a screenshot, and this affects the adb screencap
tool too - it just saves an empty file for me. This apparently doesn't prevent adb itself from capturing the screen, so being able to save a screenshot with scrcpy would be very useful.
being able to save a screenshot with scrcpy would be very useful.
Yes.
it just saves an empty file for me
What is the exact command you execute? Did you try with:
adb exec-out screencap -p > file.png
adb shell screencap -p > file.png
?
What is the exact command you execute? Did you try with:
adb exec-out adb exec-out screencap -p > file.png adb shell adb exec-out screencap -p > file.png
?
Actually, I just did
adb shell screencap -p /storage/emulated/0/Download/img.png
I'll have to see if those behave differently, though I don't understand why they would.
Edit: actually, I think you have a typo. Shouldn't those commands be:
adb exec-out screencap -p > file.png
adb shell screencap -p > file.png
Both of these result in empty files for me with protected apps. Both work on the home screen.
What is the exact command you execute? Did you try with:
adb exec-out adb exec-out screencap -p > file.png adb shell adb exec-out screencap -p > file.png
?
Actually, I just did
adb shell screencap -p /storage/emulated/0/Download/img.png
I'll have to see if those behave differently, though I don't understand why they would.
Edit: actually, I think you have a typo. Shouldn't those commands be:
adb exec-out screencap -p > file.png adb shell screencap -p > file.png
Both of these result in empty files for me with protected apps. Both work on the home screen.
I can confirm that I'm having the same issue. Is there any way to solve it?
In case anyone else is looking for a workaround:
scrcpy -r tmp.mp4
ffmpeg -i tmp.mp4 -vframes 1 sshot.png
Taking a screenshot of the streaming window is of course also an option (and results in better quality).
This pull request will capture a screenshot.
Added Save Screenshot #2040
@rom1v
Is there a way to only attain one frame or a screenshot at the time of a request?
It requires some investigations to do it in Java without an Android context (I have no time to do this now).
Sometimes, the implementation of an existing command may help:
adb exec-out screencap -p > file.png
But in this case, it's implemented in C++: https://github.com/aosp-mirror/platform_frameworks_base/blob/master/cmds/screencap/screencap.cpp
Almost there... In my case it's always generating a screenshot with a "Refreshing..." toast message, as you can see in the screenshot below:
On Windows, this can be run with PowerShell:
adb pull $(adb shell 'input keyevent 120; sleep 2s; cd /sdcard/Pictures/; find "$(pwd)" -iname "*.png" -type f -mtime -2s; sleep 4') $([environment]::getfolderpath("mypictures"))
The screenshot will be saved on your default "Pictures" folder, usually at C:\Users\%userprofile%\Pictures
Explanation to follow..
I'm late to the party but I use this to take a screenshot and generate a time base name, so we can take any number of screenshots without worrying about the name or avoiding to replace a screenshot already taken.
adb exec-out screencap -p > "screenshot_$(date +%s).png"
In my case it's always generating a screenshot with a "Refreshing..." toast message
@danfmaia - "Refreshing..." originates from React Native's Fast Refresh, which is triggered because you are outputting a new file into the current folder (which is monitored by watchman). Try writing the screenshot file to a different directory, e.g.:
adb exec-out screencap -p > "/tmp/screenshot_$(date +%s).png"
adb exec-out screencap -p > screenshot.png
produces a weird png file which cannot be open. Not in browser or other image viewer.
A normal png starts with magic number
‰PNG
(89 50 4E 47
) and the invalid png with
ÿþë�P�N�G
(FF FE EB 00 50 00 4E 00 47
).
Maybe an encoding problem? I'm on Win10. It starts with UTF BOM marker and character 2 bytes filled with zero. How to fix that?
Maybe an encoding problem? I'm on Win10. It starts with UTF BOM marker and character 2 bytes filled with zero. How to fix that?
Windows PowerShell only writes UTF-16: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_character_encoding?view=powershell-7.4#character-encoding-in-windows-powershell
Either upgrade to PowerShell (previously known as PowerShell core) or use cmd.
@yume-chan Thanks, that works. PS Core 7.4.2 (Windows Terminal) and CMD. https://learn.microsoft.com/en-us/powershell/scripting/install/installing-powershell-on-windows
I am looking for a minimalized version of what the video stream part of scrcpy does.
It is apparently not so straightforward to take a screenshot of an android device outside of the android context. I imagine similar frustrations occurred while learning how to attain the video stream. Is there a way to only attain one frame or a screenshot at the time of a request?