使用minicap对安卓手机快速截屏
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用minicap对安卓手机快速截屏相关的知识,希望对你有一定的参考价值。
参考技术A minicap属于STF框架的一个工具,由STF团队自身开发,属于较为核心的一部分,minicap运行于android设备端,负责设备屏幕视频的实时采集并通过socket接口发送.minicap采集屏幕的原理很简单:通过ndk的截屏接口不停的截屏并通过socket接口实时发送,这样客户端便可以得到一序列的图片流,图片流合成后就成为视频.
使用uiautomator2, 从截屏开始到存入电脑,大概需要1-2S.
而minicap可以把时间减少到到ms级别.
此时手机上已经安装好了 minicap 和 atx-agent,并且其实 atx-agent 应该已经启动了。
python+minicap
一、push文件至手机中
minicap 的使用有很强的针对性,针对不同架构的CPU和SDK制作了不同的 "minicap" 和 "minicap.so" 文件。
获取CPU版本
$ABI=adb shell getprop ro.product.cpu.abi
获取SDK版本
$SDK=adb shell getprop ro.build.version.sdk
1、有一种方法可以成功,可以截图成功的操作:通过adb shell进到手机对应的目录下/data/local/tmp目录下,执行 $LD_LIBRARY_PATH=/data/local/tmp /data/local/tmp/minicap -P [email protected]/0 -s > /mnt/sdcard/tmp.jpg 命令就可以,然后再将截图Pull到win电脑中。
2、如果在dos命令提示 符中,直接执行adb shell LD_LIBRARY_PATH=/data/local/tmp /data/local/tmp/minicap -P [email protected]/0 -s > c: tmp.jpg ,生成的图片就显示已损坏。
adb shell "$LD_LIBRARY_PATH=/data/local/tmp /data/local/tmp/minicap -P [email protected]/0 -s" > c: tmp.jpg
另外这是一个shell命令,直接把结果导出到windows端会存在文件头解析的问题(因为手机是linux和windows的编码格式有一些差别导致的)
import os class Banner: def __init__(self): self.Version = 0 # 版本信息 self.Length = 0 # banner长度 self.Pid = 0 # 进程ID self.RealWidth = 0 # 设备的真实宽度 self.RealHeight = 0 # 设备的真实高度 self.VirtualWidth = 0 # 设备的虚拟宽度 self.VirtualHeight = 0 #设备的虚拟高度 self.Orientation = 0 # 设备方向 self.Quirks = 0 # 设备信息获取策略 def toString(self): message = "Banner [Version=" + str(self.Version) + ", length=" + str(self.Length) + ", Pid=" + str(self.Pid) + ", realWidth=" + str(self.RealWidth) + ", realHeight=" + str(self.RealHeight) + ", virtualWidth=" + str(self.VirtualWidth) + ", virtualHeight=" + str(self.VirtualHeight) + ", orientation=" + str(self.Orientation) +", quirks=" + str(self.Quirks) + "]" return message #!/usr/bin/env python #-*- coding: utf-8 -*- ‘‘‘ Created on 2016年12月29日 @author: fengbo ‘‘‘ from Banner import Banner import socket import threading from itsdangerous import bytes_to_int from Queue import Queue 作者:PreFU 链接:https://www.jianshu.com/p/f8b8123cd062 來源:简书 简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。
class MinicapStream: __instance = None __mutex = threading.Lock() def __init__(self): self.IP = "127.0.0.1" # 定义IP self.PORT = 1313 # 监听的端口 self.Pid = 0 # 进程ID self.banner = Banner() # 用于存放banner头信息 # self.minicapSocket = socket.socket(socket.AF_INET,socket.SOCK_STREAM) self.minicapSocket=None self.ReadImageStreamTask = None self.push = None self.picture = Queue() @staticmethod def getBuilder(): """Return a single instance of TestBuilder object """ if (MinicapStream.__instance == None): MinicapStream.__mutex.acquire() if (MinicapStream.__instance == None): MinicapStream.__instance = MinicapStream() MinicapStream.__mutex.release() return MinicapStream.__instance def get_d(self): print self.picture.qsize() def run(self): #开始执行 #启动socket连接 self.minicapSocket= socket.socket(socket.AF_INET,socket.SOCK_STREAM) #定义socket类型,网络通信,TCP self.minicapSocket.connect((self.IP,self.PORT)) # return self.ReadImageStream() self.ReadImageStreamTask = threading.Thread(target=self.ReadImageStream).start() def ReadImageStream(self): #读取图片流到队列 readBannerBytes = 0 bannerLength = 2 readFrameBytes = 0 frameBodylength = 0 dataBody = "" while True: reallen=self.minicapSocket.recv(4096) length = len(reallen) if not length: continue cursor = 0 while cursor < length: #just do it if readBannerBytes < bannerLength: if readBannerBytes==0: self.banner.Version = bytes_to_int(reallen[cursor]) elif readBannerBytes==1: bannerLength = bytes_to_int(reallen[cursor]) self.banner.Length = bannerLength elif readBannerBytes in [2,3,4,5]: self.banner.Pid += (bytes_to_int(reallen[cursor]) << ((readBannerBytes - 2) * 8)) >> 0; elif readBannerBytes in [6,7,8,9]: self.banner.RealWidth += (bytes_to_int(reallen[cursor]) << ((readBannerBytes - 6) * 8)) >> 0; elif readBannerBytes in [10,11,12,13]: self.banner.RealHeight += (bytes_to_int(reallen[cursor]) << ((readBannerBytes - 10) * 8)) >> 0; elif readBannerBytes in [14,15,16,17]: self.banner.VirtualWidth += (bytes_to_int(reallen[cursor]) << ((readBannerBytes - 14) * 8)) >> 0; elif readBannerBytes in [18,19,20,21]: self.banner.VirtualHeight += (bytes_to_int(reallen[cursor]) << ((readBannerBytes - 18) * 8)) >> 0; elif readBannerBytes == 22: self.banner.Orientation = bytes_to_int(reallen[cursor])*90 elif readBannerBytes == 23: self.banner.Quirks = bytes_to_int(reallen[cursor]) cursor += 1 readBannerBytes += 1 if readBannerBytes == bannerLength: print self.banner.toString() elif readFrameBytes < 4: frameBodylength =frameBodylength+ ((bytes_to_int(reallen[cursor])<<(readFrameBytes*8)) >> 0) cursor += 1 readFrameBytes += 1 else: if length - cursor >= frameBodylength: dataBody = dataBody + reallen[cursor:(cursor+frameBodylength)] if bytes_to_int(dataBody[0])!=0xFF or bytes_to_int(dataBody[1])!=0xD8: return self.picture.put(dataBody) # self.save_file(‘d:/pic.png‘, dataBody) cursor += frameBodylength frameBodylength = 0 readFrameBytes = 0 dataBody = "" else: dataBody = dataBody + reallen[cursor:length] frameBodylength -= length - cursor; readFrameBytes += length - cursor; cursor = length; # adb shell LD_LIBRARY_PATH=/data/local/tmp /data/local/tmp/minicap -P [email protected]/0 # adb forward tcp:1313 localabstract:minicap def save_file(self,file_name, data): file=open(file_name, "wb") file.write(data) file.flush() file.close() if __name__ == ‘__main__‘: a = MinicapStream.getBuilder() print id(a) a.run() # print a.picture 作者:PreFU 链接:https://www.jianshu.com/p/f8b8123cd062 來源:简书 简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。
############################################################################# ## ## Copyright (C) 2010 Riverbank Computing Limited. ## Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). ## All rights reserved. ## ## This file is part of the examples of PyQt. ## ## $QT_BEGIN_LICENSE:BSD$ ## You may use this file under the terms of the BSD license as follows: ## ## "Redistribution and use in source and binary forms, with or without ## modification, are permitted provided that the following conditions are ## met: ## * Redistributions of source code must retain the above copyright ## notice, this list of conditions and the following disclaimer. ## * Redistributions in binary form must reproduce the above copyright ## notice, this list of conditions and the following disclaimer in ## the documentation and/or other materials provided with the ## distribution. ## * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor ## the names of its contributors may be used to endorse or promote ## products derived from this software without specific prior written ## permission. ## ## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ## $QT_END_LICENSE$ ## ############################################################################# from PyQt4 import QtCore, QtGui from MinicapStream import MinicapStream import threading from time import sleep class Screenshot(QtGui.QWidget): def __init__(self): super(Screenshot, self).__init__() self.obje = MinicapStream.getBuilder() self.inst() self.picture = self.obje.picture self.screenshotLabel = QtGui.QLabel() self.screenshotLabel.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding) self.screenshotLabel.setAlignment(QtCore.Qt.AlignCenter) self.screenshotLabel.setScaledContents(True) self.screenshotLabel.setMinimumSize(400, 640) self.createOptionsGroupBox() self.createButtonsLayout() mainLayout = QtGui.QVBoxLayout() mainLayout.addWidget(self.screenshotLabel) mainLayout.addWidget(self.optionsGroupBox) mainLayout.addLayout(self.buttonsLayout) self.setLayout(mainLayout) self.shootScreen() self.delaySpinBox.setValue(5) self.setWindowTitle("Screenshot") self.resize(480,640) # def resizeEvent(self, event): # scaledSize = self.originalPixmap.size() # scaledSize.scale(self.screenshotLabel.size(), QtCore.Qt.KeepAspectRatio) # if not self.screenshotLabel.pixmap() or scaledSize != self.screenshotLabel.pixmap().size(): # self.updateScreenshotLabel() def inst(self): t= threading.Thread(target=self.obje.run) t.start() def newScreenshot(self): while True: if not self.picture.empty(): sleep(0.1) self.shootScreen() else: sleep(0.2) # if self.hideThisWindowCheckBox.isChecked(): # self.hide() # self.newScreenshotButton.setDisabled(True) # # QtCore.QTimer.singleShot(self.delaySpinBox.value() * 1000, # self.shootScreen) def saveScreenshot(self): format = ‘png‘ initialPath = QtCore.QDir.currentPath() + "/untitled." + format fileName = QtGui.QFileDialog.getSaveFileName(self, "Save As", initialPath, "%s Files (*.%s);;All Files (*)" % (format.upper(), format)) # if fileName: # self.originalPixmap.save(fileName, format) def shootScreen(self): if self.delaySpinBox.value() != 0: QtGui.qApp.beep() # Garbage collect any existing image first. self.originalPixmap = None # self.originalPixmap = QtGui.QPixmap.grabWindow(QtGui.QApplication.desktop().winId()) # self.updateScreenshotLabel() pixmap=QtGui.QPixmap() pixmap.loadFromData(self.picture.get()) self.screenshotLabel.setPixmap(pixmap); self.newScreenshotButton.setDisabled(False) if self.hideThisWindowCheckBox.isChecked(): self.show() def updateCheckBox(self): if self.delaySpinBox.value() == 0: self.hideThisWindowCheckBox.setDisabled(True) else: self.hideThisWindowCheckBox.setDisabled(False) def createOptionsGroupBox(self): self.optionsGroupBox = QtGui.QGroupBox("Options") self.delaySpinBox = QtGui.QSpinBox() self.delaySpinBox.setSuffix(" s") self.delaySpinBox.setMaximum(60) self.delaySpinBox.valueChanged.connect(self.updateCheckBox) self.delaySpinBoxLabel = QtGui.QLabel("Screenshot Delay:") self.hideThisWindowCheckBox = QtGui.QCheckBox("Hide This Window") optionsGroupBoxLayout = QtGui.QGridLayout() optionsGroupBoxLayout.addWidget(self.delaySpinBoxLabel, 0, 0) optionsGroupBoxLayout.addWidget(self.delaySpinBox, 0, 1) optionsGroupBoxLayout.addWidget(self.hideThisWindowCheckBox, 1, 0, 1, 2) self.optionsGroupBox.setLayout(optionsGroupBoxLayout) def createButtonsLayout(self): self.newScreenshotButton = self.createButton("New Screenshot", self.newScreenshot) self.saveScreenshotButton = self.createButton("Save Screenshot", self.saveScreenshot) self.quitScreenshotButton = self.createButton("Quit", self.close) self.buttonsLayout = QtGui.QHBoxLayout() self.buttonsLayout.addStretch() self.buttonsLayout.addWidget(self.newScreenshotButton) self.buttonsLayout.addWidget(self.saveScreenshotButton) self.buttonsLayout.addWidget(self.quitScreenshotButton) def createButton(self, text, member): button = QtGui.QPushButton(text) button.clicked.connect(member) return button def updateScreenshotLabel(self): self.screenshotLabel.setPixmap(self.originalPixmap.scaled( self.screenshotLabel.size(), QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation)) if __name__ == ‘__main__‘: import sys app = QtGui.QApplication(sys.argv) screenshot = Screenshot() screenshot.show() sys.exit(app.exec_()) 作者:PreFU 链接:https://www.jianshu.com/p/f8b8123cd062 來源:简书 简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。
以上是关于使用minicap对安卓手机快速截屏的主要内容,如果未能解决你的问题,请参考以下文章