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

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

Open
toddsierens opened this issue Aug 2, 2019 · 22 comments

Comments

@toddsierens
Copy link
Contributor

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?

@hasansidd
Copy link

does ascreenshot not accomplish what you need?

@rom1v
Copy link
Collaborator

rom1v commented Aug 7, 2019

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

@toddsierens
Copy link
Contributor Author

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.

@hasansidd
Copy link

hasansidd commented Aug 8, 2019

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.

@toddsierens
Copy link
Contributor Author

toddsierens commented Aug 9, 2019

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 toddsierens reopened this Aug 9, 2019
@rayworks
Copy link

rayworks commented Aug 15, 2019

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

@afontenot
Copy link

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.

@rom1v
Copy link
Collaborator

rom1v commented Sep 11, 2019

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

?

@afontenot
Copy link

afontenot commented Sep 11, 2019

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.

@SGA-abmajid-nurulaiman
Copy link

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?

@rom1v
Copy link
Collaborator

rom1v commented Nov 14, 2019

@freb
Copy link

freb commented Nov 25, 2020

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

@Spudmn
Copy link

Spudmn commented Jan 16, 2021

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

@danfmaia
Copy link

danfmaia commented Jul 20, 2021

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

@4nric
Copy link

4nric commented Nov 27, 2021

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

@BlaShadow
Copy link

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"

@friederbluemle
Copy link

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"

@SmartManoj
Copy link

@rom1v rom1v mentioned this issue Mar 6, 2024
1 task
@fudom
Copy link

fudom commented May 24, 2024

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?

@yume-chan
Copy link
Contributor

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.

@fudom
Copy link

fudom commented Jun 3, 2024

@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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests