scrcpy icon indicating copy to clipboard operation
scrcpy copied to clipboard

Is there a way to capture only a frame or screenshot rather than a video stream?

Open toddsierens opened this issue 6 years ago • 21 comments
trafficstars

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?

toddsierens avatar Aug 02 '19 15:08 toddsierens

does ascreenshot not accomplish what you need?

hasansidd avatar Aug 07 '19 15:08 hasansidd

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

rom1v avatar Aug 07 '19 15:08 rom1v

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.

toddsierens avatar Aug 08 '19 13:08 toddsierens

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.

hasansidd avatar Aug 08 '19 15:08 hasansidd

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 avatar Aug 09 '19 20:08 toddsierens

@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.

rayworks avatar Aug 15 '19 14:08 rayworks

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.

afontenot avatar Sep 11 '19 19:09 afontenot

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

?

rom1v avatar Sep 11 '19 20:09 rom1v

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.

afontenot avatar Sep 11 '19 22:09 afontenot

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?

SGA-abmajid-nurulaiman avatar Oct 14 '19 05:10 SGA-abmajid-nurulaiman

https://android.googlesource.com/platform/frameworks/base/+/android10-release/core/java/android/view/SurfaceControl.java#1880

rom1v avatar Nov 04 '19 15:11 rom1v

https://github.com/lcacheux/screenshotserver

rom1v avatar Nov 14 '19 14:11 rom1v

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).

freb avatar Nov 25 '20 23:11 freb

This pull request will capture a screenshot.
Added Save Screenshot #2040

Spudmn avatar Jan 16 '21 07:01 Spudmn

@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:

danfmaia avatar Jul 20 '21 22:07 danfmaia

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..

4nric avatar Nov 27 '21 14:11 4nric

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"

BlaShadow avatar Dec 10 '21 15:12 BlaShadow

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"

friederbluemle avatar May 25 '22 06:05 friederbluemle

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?

fudom avatar May 24 '24 08:05 fudom

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 avatar May 24 '24 19:05 yume-chan

@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

fudom avatar Jun 03 '24 10:06 fudom

Damn, today I noticed that this feature is not implemented yet; definitely it would be awesome take a screenshot using a hotkey for scrcpy.

hellishvictor avatar Mar 13 '25 23:03 hellishvictor

So, uhm, what's happening? Is it possible to save screenshots and images somewhere in a desktop folder?

osh91 avatar Apr 16 '25 19:04 osh91