来自 adb 的 Nexus One 的屏幕截图?

Posted

技术标签:

【中文标题】来自 adb 的 Nexus One 的屏幕截图?【英文标题】:Screenshot of the Nexus One from adb? 【发布时间】:2011-02-17 22:07:45 【问题描述】:

我的目标是能够键入一个单词命令并从通过 USB 连接的根 Nexus One 中获取屏幕截图。

到目前为止,我可以像这样拉取我认为是32bit xRGB888 原始图像的帧缓冲区:

adb pull /dev/graphics/fb0 fb0

不过,我很难将其转换为 png。我正在尝试这样的ffmpeg:

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

这会创建一个可爱的紫色图像,其中的部分与屏幕有点相似,但这绝不是一个干净的屏幕截图。

【问题讨论】:

【参考方案1】:

ICS 的一个更简单的解决方案是从命令行使用以下命令

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

这会将 screenshot.png 文件保存在当前目录中。

在运行 4.0.3 的三星 Galaxy SII 和 SII 上测试。

【讨论】:

更快:adb shell screencap -p \| uuencode o | uudecode -o out.png(需要 linux+uudecode,base64 也可以) @ce4,您应该写下您的评论作为答案。这是这个问题的最佳答案。 @Benjamin 这个问题已经有 >10 个答案了......而且它只是对 Ben 的答案的改进,仅适用于 linux 用户。 @ce4 的回答对我来说每张照片需要多花 1-2 秒的时间,所以 YMMV(除非意图是更快地打字)。【参考方案2】:

其实,还有一个非常简单的功能可以从你的安卓设备上截取屏幕截图:像这样编写简单的脚本1.script

# 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')

并致电monkeyrunner 1.script

【讨论】:

【参考方案3】:

N1的frame buffer好像使用了RGB32编码(每像素32位)。

这是我使用 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

从此处描述的 ADP1 方法派生的另一种方法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 脚本'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])

【讨论】:

【参考方案4】:

使用我的 HTC Hero(因此从 480x800 调整到 320x480),如果我使用 rgb565 而不是 8888,这将有效:

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

【讨论】:

是的,它适用于我的 Magic/Dream/Hero,但不适用于 N1。不过还是谢谢。 也适用于 SE Xperia X10。【参考方案5】:

如果你安装了dos2unix,那么下面

adb shell screencap -p | dos2unix > screen.png

【讨论】:

这是我最喜欢的方法——非常简单!我为除文件名之外的所有内容设置了别名,以便在需要截屏时轻松使用。 这可能只对 Windows 准确,默认情况下它会在其 C 运行时内“帮助”将 LF 转换为 CRLF。在其他操作系统上,在那里运行 dos2unix 会损坏 png【参考方案6】:

我相信迄今为止所有的帧缓冲区都是 RGB 565,而不是 888。

【讨论】:

【参考方案7】:

现在我们有一个单行命令来截取屏幕截图。命令如下:

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

在您的终端中输入上述命令并按回车键。如果您希望将屏幕截图存储在任何特定位置,请在screen.png 之前给出路径(或)目录。

Source.

【讨论】:

谢谢佩德罗·巴西莱罗爵士。【参考方案8】:

我认为 rgb32torgb888.py 应该是

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

【讨论】:

是的,我必须交换 R 和 B 通道才能从 N1 获得正确的颜色。【参考方案9】:

我希望我的脚本可能有用。我在我的银河选项卡上使用它,它运行良好,但您可以更改默认分辨率。不过,它需要“zsh”外壳:

#!/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 $VRESx$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.$$

【讨论】:

我不是很清楚——如果你提供分辨率,它(可能)可以在任何安卓上工作。【参考方案10】:

在 MyTouch Slide 3G 上,我最终在屏幕截图中交换了红色和蓝色通道。这是在这种情况下其他任何人的正确 ffmpeg 咒语: (值得注意的部分:-pix_fmt bgr32

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

感谢 Patola 提供的方便的 shell 脚本!至少对于我的手机而言,无需修改即可正确定向纵向模式 (320x480),因此他的脚本结尾变为:

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

【讨论】:

【参考方案11】:

rgb565 代替 8888 也适用于模拟器

【讨论】:

【参考方案12】:

有点复杂/过度,但它同时处理屏幕截图和帧缓冲区场景(以及计算分辨率)。

#!/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=$HRESx$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

【讨论】:

【参考方案13】:

这可能与问题Reading binary Data from adb shell's stdout 有关,其中 adb 尝试为您进行 LF 到 CRLF 的转换(它可能只是 adb 的 Windows 版本)。我个人在将 \n 转换为 \r\r\n 时遇到了麻烦,因此作为一种转换方式,最好使用 [1] 处的代码或使用。

对我来说(在 cygwin 中)运行它: adb shell 'cat /dev/graphics/fb0' | perl -pi -e 's/\r\r\n/\n/g' 似乎有帮助

除此之外,尝试将宽度和高度与文件的大小进行比较。文件大小应该可以被Width * height 整除,如果不是这种情况,那么 adb 工具会自动为您做事,或者它是一种比 rgb545 或 rgb8888 更奇特的格式。

如果只是颜色问题(即:结果图像中的所有内容都在正确的位置),那么您可能需要考虑交换红色和蓝色通道,因为某些系统(通常)使用字节顺序 BGRA 而不是 RGBA。

【讨论】:

【参考方案14】:

完全自动化此过程的一种方法是创建一个脚本,将当前时间戳添加到文件名中。这样,你就不用自己写文件名了,你所有的截图都有不同的名字,而且你的截图是按时间排序的。

bash 脚本示例:

#! /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

这将创建一个名为 screenshot_2014-01-07-10:31.png 的文件

【讨论】:

以上是关于来自 adb 的 Nexus One 的屏幕截图?的主要内容,如果未能解决你的问题,请参考以下文章

当我尝试使用 adb shell 打开数据库时,为啥在根植的 Nexus One 上出现“sqlite3:未找到”错误?

sh Android设备带有ADB屏幕截图 - 来自http://blog.shvetsov.com/2013/02/grab-android-screenshot-to-computer-via.h

Nexus 5 不会使用 ADB 进入打盹模式

如何在 Android Studio 的屏幕截图中添加更多框架,如 Nexus 6P、5X 等

使用adb命令对手机屏幕截图

使用adb命令对手机屏幕截图