Friday, April 27, 2012

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 screen, but it's by no means a clean screenshot.


Source: Tips4all

9 comments:

  1. You can simply build this app from the Android source tree: http://android.git.kernel.org/?p=platform/sdk.git;a=tree;f=screenshot;h=d3bfa8c5d4ba1a6b4ac0d7d6f01f8df288917d4e;hb=master

    It's a command line utility that grabs a screenshot from the device.

    ReplyDelete
  2. 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])

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

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

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

    ReplyDelete
  6. I think rgb32torgb888.py should be

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

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

    ReplyDelete
  8. 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.$$

    ReplyDelete
  9. Here's another solution, handling various color depths: RGB16, RGB24, RGB32: http://www.pocketmagic.net/?p=1473

    ReplyDelete