开发者

Screenshot of the Nexus One from adb?

My goal is to be able to type a one word command and get a screenshot from a rooted Nexus One attached by USB.

So far, I can get the framebuffer which I believe is a 32bit xRGB888 raw image by pulling it like this:

adb pull /dev/graphics/fb0 fb0

From there though, I'm having a hard time getting it converted to a png. I'm trying with ffmpeg like this:

ffmpeg -vframes 1 -vcodec rawvideo -f rawvideo -pix_fmt rgb8888 -s 480x800 -i fb0 -f image2 -vcodec png image.png

That creates a lovely purple image that has parts that vaguely resemble the scree开发者_运维百科n, but it's by no means a clean screenshot.


A vastly easier solution for ICS is to use the following from the command line

adb shell /system/bin/screencap -p /sdcard/screenshot.png
adb pull /sdcard/screenshot.png screenshot.png

This'll save the screenshot.png file in the current directory.

Tested on a Samsung Galaxy SII & SII running 4.0.3.


Actually, there is another very simple ability to grab screenshot from your android device: write simple script 1.script like this:

# Imports the monkeyrunner modules used by this program
from com.android.monkeyrunner import MonkeyRunner, MonkeyDevice

# Connects to the current device, returning a MonkeyDevice object
device = MonkeyRunner.waitForConnection()

# Takes a screenshot
result = device.takeSnapshot()

# Writes the screenshot to a file
result.writeToFile('1.png','png')

and call monkeyrunner 1.script.


It seems that frame buffer of N1 uses RGB32 encoding (32 bits per pixel).

Here is my script using ffmpeg:

adb pull /dev/graphics/fb0 fb0
dd bs=1920 count=800 if=fb0 of=fb0b
ffmpeg -vframes 1 -vcodec rawvideo -f rawvideo -pix_fmt rgb32 -s 480x800 -i fb0b -f image2 -vcodec png fb0.png

Another way derived from ADP1 method described here http://code.lardcave.net/entries/2009/07/27/132648/

adb pull /dev/graphics/fb0 fb0
dd bs=1920 count=800 if=fb0 of=fb0b
python rgb32torgb888.py <fb0b >fb0b.888
convert -depth 8 -size 480x800 RGB:fb0b.888 fb0.png

Python script 'rgb32torgb888.py':

import sys
while 1:
 colour = sys.stdin.read(4)
 if not colour:
  break
 sys.stdout.write(colour[2])
 sys.stdout.write(colour[1])
 sys.stdout.write(colour[0])


Using my HTC Hero (and hence adjusting from 480x800 to 320x480), this works if I use rgb565 instead of 8888:

ffmpeg -vframes 1 -vcodec rawvideo -f rawvideo -pix_fmt rgb565 -s 320x480 -i fb0 -f image2 -vcodec png image.png


If you have dos2unix installed, then the below

adb shell screencap -p | dos2unix > screen.png


I believe all framebuffers to date are RGB 565, not 888.


Now we have got a single line command to take a screenshot. The command as follows:

adb shell screencap -p | perl -pe 's/\x0D\x0A/\x0A/g' > screen.png

Type the above command in your terminal and press enter. If you want the screenshot to be stored in any specific location, then give the path (or) directory before screen.png.

Source.


I think rgb32torgb888.py should be

 sys.stdout.write(colour[0])
 sys.stdout.write(colour[1])
 sys.stdout.write(colour[2])


I hope my script might be of any use. I use it on my galaxy tab and it works perfectly, but it is possible for you to change the default resolution. It requires the "zsh" shell, though:

#!/bin/zsh

# These settings are for the galaxy tab.
HRES=600
VRES=1024

usage() {
  echo "Usage: $0 [ -p ] outputfile.png"
  echo "-- takes screenshot off your Galaxy Tab Android phone."
  echo " -p: portrait mode"
  echo " -r X:Y: specify resolution, e.g. -r 480:640 specifies that your cellphone has 480x640 resolution."
  exit 1
}

PORTRAIT=0 # false by default

umask 022

[[ ! -w . ]] && {
  echo "*** Error: current directory not writeable."
  usage
}

[[ ! -x $(which mogrify) ]] && {
  echo "*** Error: ImageMagick (mogrify) is not in the PATH!"
  usage
}

while getopts "pr:" myvar
do
  [[ "$myvar" == "p" ]] && PORTRAIT=1
  [[ "$myvar" == "r" ]] && {
    testhres="${OPTARG%%:*}" # remove longest-matching :* from end
    testvres="${OPTARG##*:}" # remove longest-matchung *: from beginning
    if [[ $testhres == <0-> && $testvres == <0-> ]] # Interval: from 0 to infinite. Any value would be: <->
    then
      HRES=$testhres
      VRES=$testvres
    else
      echo "Error! One of these values - '${testhres}' or '${testvres}' - is not numeric!"
      usage
    fi
  }
done
shift $((OPTIND-1))

[[ $# < 1 ]] && usage

outputfile="${1}"

blocksize=$((HRES*4))
count=$((VRES))

adb pull /dev/graphics/fb0 fb0.$$
/bin/dd bs=$blocksize count=$count if=fb0.$$ of=fb0b.$$
/usr/bin/ffmpeg -vframes 1 -vcodec rawvideo -f rawvideo -pix_fmt rgb32 -s ${VRES}x${HRES} -i fb0b.$$ -f image2 -vcodec png "${outputfile}"

if (( ${PORTRAIT} ))
then
  mogrify -rotate 270 "${outputfile}"
else
  mogrify -flip -flop "${outputfile}"
fi

/bin/rm -f fb0.$$ fb0b.$$


On the MyTouch Slide 3G, I ended up with the red and blue channels swapped in my screenshots. Here's the correct ffmpeg incantation for anyone else in that situation: (the notable part: -pix_fmt bgr32)

ffmpeg -vframes 1 -vcodec rawvideo -f rawvideo -pix_fmt bgr32 -s 320x480 -i fb0 -f image2 -vcodec png image.png

Thanks to Patola for the handy shell script! At least for my phone, no mogrification is necessary to correctly orient for portrait mode (320x480), and so the end of his script becomes:

# assuming 'down' is towards the keyboard or usb jack 
# in landscape and protrait modes respectively
(( ${PORTRAIT} )) || mogrify -rotate 270 "${outputfile}"

/bin/rm -f fb0.$$ fb0b.$$


rgb565 instead of 8888 also work on emulator


A little elaborate/excessive but it handles both screencap and framebuffer scenarios (as well as figuring out the resolution too).

#!/bin/bash
#
# adb-screenshot - simple script to take screenshots of android devices
#
# Requires: 'ffmpeg' and 'adb' to be somewhere in the PATH
#
# Author: Kevin C. Krinke <kevin@krinke.ca>
# License: Public Domain

# globals / constants
NAME=$(basename $0)
TGT=~/Desktop/${NAME}.png
SRC=/sdcard/${NAME}.png
TMP=/tmp/${NAME}.$$
RAW=/tmp/${NAME}.raw
FFMPEG=$(which ffmpeg)
ADB=$(which adb)
DD=$(which dd)
USB_DEVICE=""

# remove transitory files if exist
function cleanup () {
    [ -f "${RAW}" ] && rm -f "${RAW}"
    [ -f "${TMP}" ] && rm -f "${TMP}"
    [ -z "$1" ] && die "aborting process now."
    exit 0
}

# exit with an error
function die () {
    echo "Critical Error: $@"
    exit 1
}

# catch all signals and cleanup / dump
trap cleanup \
    SIGHUP SIGINT SIGQUIT SIGILL SIGTRAP SIGABRT SIGEMT SIGFPE \
    SIGKILL SIGBUS SIGSEGV SIGSYS SIGPIPE SIGALRM SIGTERM SIGURG \
    SIGSTOP SIGTSTP SIGCONT SIGCHLD SIGTTIN SIGTTOU SIGIO SIGXCPU \
    SIGXFSZ SIGVTALRM SIGPROF SIGWINCH SIGINFO SIGUSR1 SIGUSR2

# adb is absolutely required
[ -x "${ADB}" ] || die "ADB is missing!"

# cheap getopt
while [ $# -gt 0 ]
do
    case "$1" in
        "-h"|"--help")
            echo "usage: $(basename $0) [-h|--help] [-s SERIAL] [/path/to/output.png]"
            exit 1
            ;;
        "-s")
            [ -z "$2" ] && die "Missing argument for option \"-s\", try \"${NAME} --help\""
            HAS_DEVICE=$(${ADB} devices | grep "$2" )
            [ -z "${HAS_DEVICE}" ] && die "No device found with serial $2"
            USB_DEVICE="$2"
            ;;
        *)
            [ -n "$1" -a -d "$(dirname $1)" ] && TGT="$1"
            ;;
    esac
    shift
done

# prep target with fire
[ -f "${TGT}" ] && rm -f "${TGT}"

# tweak ADB command line
if [ -n "${USB_DEVICE}" ]
then
    ADB="$(which adb) -s ${USB_DEVICE}"
fi

# calculate resolution
DISPLAY_RAW=$(${ADB} shell dumpsys window)
HRES=$(echo "${DISPLAY_RAW}" | grep SurfaceWidth  | head -1 | perl -pe 's/^.*\bSurfaceWidth\:\s*(\d+)px\b.*$/$1/')
VRES=$(echo "${DISPLAY_RAW}" | grep SurfaceHeight | head -1 | perl -pe 's/^.*\bSurfaceHeight\:\s*(\d+)px\b.*$/$1/')
RES=${HRES}x${VRES}

# check for screencap binary
HAS_SCREENCAP=$(${ADB} shell "[ -x /system/bin/screencap ] && echo 1 || echo 0" | perl -pe 's/\D+//g')
if [ "$HAS_SCREENCAP" == "1" ]
then # use screencap to get the image easy-peasy
    echo -n "Getting ${RES} screencap... "
    ( ${ADB} shell /system/bin/screencap ${SRC} 2>&1 ) > /dev/null
    [ "$?" != "0" ] && die "Failed to execute screencap"
    ( ${ADB} pull ${SRC} ${TMP} 2>&1 ) > /dev/null
    [ "$?" != "0" ] && die "Failed to pull png image"
    ( ${ADB} shell rm ${SRC} 2>&1 ) > /dev/null
    [ "$?" != "0" ] && die "Failed to remove png image"
    mv ${TMP} ${TGT}
    echo "wrote: ${TGT}"
else # fetch a framebuffer snapshot
    # ffmpeg is only needed if device is pre-ICS
    [ -x "${FFMPEG}" ] || die "FFMPEG is missing!"
    [ -x "${DD}" ] || die "DD is missing!"
    echo -n "Getting ${RES} framebuffer... "
    ( ${ADB} pull /dev/graphics/fb0 ${RAW} 2>&1 ) > /dev/null
    [ "$?" != "0" ] && die "Failed to pull raw image data"
    # calculate dd parameters
    COUNT=$((HRES*4))
    BLOCKSIZE=$((VRES))
    ( ${DD} bs=${BLOCKSIZE} count=${COUNT} if=${RAW} of=${TMP} 2>&1 ) > /dev/null
    [ "$?" != "0" ] && die "Failed to realign raw image data"
    ( ${FFMPEG} -vframes 1 -vcodec rawvideo -f rawvideo -pix_fmt rgb32 -s ${RES} -i ${TMP} -f image2 -vcodec png ${TGT} 2>&1 ) > /dev/null
    [ "$?" != "0" ] && die "Failed to encode PNG image"
    echo "wrote: ${TGT}"
fi

# exit app normal
cleanup 1


This might be related to the issue Reading binary Data from adb shell's stdout where adb attempts to do LF to CRLF conversion for you (it's probably just the windows version of adb). I personally had trouble with it conversion \n to \r\r\n so as a way to convert this it's good to either to use the code at [1] or to to use.

for me running it with (in cygwin): adb shell 'cat /dev/graphics/fb0' | perl -pi -e 's/\r\r\n/\n/g' seemed to help

other than that try comparing the Width & height to the size of the file. File size should be evenly divisible by Width * height if that's not the case then either the adb tool is automatically doing things for you or it's a more exotic format then rgb545 or rgb8888.

if it's just a color issue (ie: everything in the result image is in the right spot) then you might want to consider swapping the Red & Blue channels as some systems (in general) use byte order BGRA instead of RGBA.


A way to completely automatize this process is to create a script which adds the curent timestamp to the filename. This way, you don't have to write the filename yourself, all your screenshots have a different name, and your screenshots are sorted by time.

Example of bash script :

#! /bin/bash

filename=$(date +"_%Y-%m-%d-%H:%M")

/PATH_TO_ANDROID_SDK/platform-tools/adb -d shell screencap -p | perl -pe 's/\x0D\x0A/\x0A/g' > screenshot$filename.png

This will create a file named like screenshot_2014-01-07-10:31.png

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜