android 截屏 和屏幕录像
2014-08-31 22:28
246 查看
android上获取屏幕内容的方法有以下几种:
1.调用view的getDrawingCache接口,得到屏幕内容的bitmap;优点是使用方便,缺点是只能获取单个view的图像内容,如果此view被切换到后台,或者屏幕上还有别的view,那获取的内容都不是你在屏幕上看到的全屏的图像。
使用此方法先要通过setDrawingCacheEnable方法把cache开启,然后再调用getDrawingCache方法就可以获得view的cache图片了。buildDrawingCache方法可以不用调用,因为调用getDrawingCache方法时,若果cache没有建立,系统会自动调用buildDrawingCache方法生成cache。若果要更新cache, 必须要调用destoryDrawingCache方法把旧的cache销毁,才能建立新的。
当调用setDrawingCacheEnabled方法设置为false, 系统也会自动把原来的cache销毁。
2.调用系统截屏接口。android4.0以后系统自带截屏功能,使用方法是按下 power+volume - ,在同时按下2键并保持0.5s左右后,
会听到咔嚓一声响声,并弹出一个浮动动画,显示截图效果。我们在代码里可以模拟按键的方式来调用这个接口实现截屏,例如:
public void testScreenshot() throws Exception {
Log.d(LOG_TAG, "starting testScreenshot");
// launch the activity.
ScreenshotStubActivity activity = getActivity();
assertNotNull(activity);
File screenshotDir = getScreenshotDir();
NewScreenshotObserver observer = new NewScreenshotObserver(
screenshotDir.getAbsolutePath());
observer.startWatching();
takeScreenshot();
// unlikely, but check if a new screenshot file was already created
if (observer.getCreatedPath() == null) {
// wait for screenshot to be created
synchronized(observer) {
observer.wait(SCREEN_WAIT_TIME_SEC*1000);
}
}
assertNotNull(String.format("Could not find screenshot after %d seconds",
SCREEN_WAIT_TIME_SEC), observer.getCreatedPath());
File screenshotFile = new File(screenshotDir, observer.getCreatedPath());
try {
assertTrue(String.format("Detected new screenshot %s but its not a file",
screenshotFile.getName()), screenshotFile.isFile());
assertTrue(String.format("Detected new screenshot %s but its not an image",
screenshotFile.getName()), isValidImage(screenshotFile));
} finally {
// delete the file to prevent external storage from filing up
screenshotFile.delete();
}
}
private void takeScreenshot() {
getInstrumentation().sendKeySync(new KeyEvent(KeyEvent.ACTION_DOWN,
KeyEvent.KEYCODE_POWER));
getInstrumentation().sendKeySync(new KeyEvent(KeyEvent.ACTION_DOWN,
KeyEvent.KEYCODE_VOLUME_DOWN));
// the volume down key event will cause the 'volume adjustment' UI to appear in the
// foreground, and steal UI focus
// unfortunately this means the next key event will get directed to the
// 'volume adjustment' UI, instead of this test's activity
// for this reason this test must be signed with platform certificate, to grant this test
// permission to inject key events to another process
getInstrumentation().sendKeySync(new KeyEvent(KeyEvent.ACTION_UP,
KeyEvent.KEYCODE_VOLUME_DOWN));
getInstrumentation().sendKeySync(new KeyEvent(KeyEvent.ACTION_UP,
KeyEvent.KEYCODE_POWER));
}
此方法的实现的方法比较简单,代码流程如下:
2.1 按下 volume down+power;
2.2 PhoneWindowManager.java
里捕获按键,当VOLUME_DOWN和Power键被同时按下后,向SystemUI发送截屏Message;
2.3 SystemUI(TakeScreenshotService.java和GlobalScreenshot.java)收到来自Client端的截屏Message后,调用Surface.java的方法 screenshot()截屏,并将截取到的图片通过WindowManager以浮动窗口的形式显示给用户查看。screenshot()方法是@hide的,sdk里无法直接调用。
2.4 surface.java 调用JNI的android_view_Surface.cpp中的doScreenshot(...)方法,最终调用opengl的接口来获取屏幕图像:
-> [android_view_Surface.cpp] doScreenshot(...);
->update(...);
-> [SurfaceComposerClinet.cpp] update(...);
-> [SurfaceFlinger.cpp] captureScreen(...);
->captureScreenImplLocked(...);
->glReadPixels(...);
3.直接调用ComposerService的captureScreen接口,获取屏幕的rgb数据。此方法直接获取fb里的内容,是最灵活的,可实现屏幕录像;缺点是使用上最麻烦。示例代码如下:
frameworks\base\services\surfaceflinger\tests\screencap\screencap.cpp
#include <utils/Log.h>
#include <binder/IPCThreadState.h>
#include <binder/ProcessState.h>
#include <binder/IServiceManager.h>
#include <binder/IMemory.h>
#include <surfaceflinger/ISurfaceComposer.h>
#include <SkImageEncoder.h>
#include <SkBitmap.h>
using namespace android;
int main(int argc, char** argv)
{
if (argc != 2) {
printf("usage: %s path\n", argv[0]);
exit(0);
}
const String16 name("SurfaceFlinger");
sp<ISurfaceComposer> composer;
getService(name, &composer);
sp<IMemoryHeap> heap;
uint32_t w, h;
PixelFormat f;
status_t err = composer->captureScreen(0, &heap, &w, &h, &f, 0, 0);
if (err != NO_ERROR) {
fprintf(stderr, "screen capture failed: %s\n", strerror(-err));
exit(0);
}
printf("screen capture success: w=%u, h=%u, pixels=%p\n",
w, h, heap->getBase());
printf("saving file as PNG in %s ...\n", argv[1]);
SkBitmap b;
b.setConfig(SkBitmap::kARGB_8888_Config, w, h);
b.setPixels(heap->getBase());
SkImageEncoder::EncodeFile(argv[1], b, SkImageEncoder::kPNG_Type, SkImageEncoder::kDefaultQuality);
return 0;
}
如果想在你的apk里使用,需要添加权限:
<SPAN style="FONT-SIZE: 24px"><uses-permission android:name="android.permission.READ_FRAME_BUFFER" /></SPAN>
方法3实际上和方法2的原理一样,但是结果不一样,方法3可获取到rgb颜色数据,而不是图片。
4.使用opengl接口获取屏幕内容
4.1 java接口
示例代码如下:
public static Bitmap SavePixels(int x, int y, int w, int h, GL10 gl)
{
int b[]=new int[w*h];
int bt[]=new int[w*h];
IntBuffer ib=IntBuffer.wrap(b);
ib.position(0);
gl.glReadPixels(x, y, w, h, GL10.GL_RGBA, GL10.GL_UNSIGNED_BYTE, ib);
for(int i=0; i<h; i++)
{
for(int j=0; j<w; j++)
{
int pix=b[i*w+j];
int pb=(pix>>16)&0xff;
int pr=(pix<<16)&0x00ff0000;
int pix1=(pix&0xff00ff00) | pr | pb;
bt[(h-i-1)*w+j]=pix1;
}
}
Bitmap sb=Bitmap.createBitmap(bt, w, h, true);
return sb;
}
上面是一个简单的GL读取RGBA分量的方法,然后生成Android通用的Bitmap对象。
public static void SavePNG(int x, int y, int w, int h, String fileName, GL10 gl)
{
Bitmap bmp=SavePixels(x,y,w,h,gl);
try
{
FileOutputStream fos=new FileOutputStream("/sdcard/android123/"+fileName); //如何2.2或更高的系统sdcard路径为/mnt/sdcard/
bmp.compress(CompressFormat.PNG, 100, fos); //保存为png格式,质量100%
try
{
fos.flush();
}
catch (IOException e)
{
e.printStackTrace();
}
try
{
fos.close();
}
catch (IOException e)
{
e.printStackTrace();
}
}
catch (FileNotFoundException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
4.2 native接口
示例代码如下:
unsigned char* buffer[height * width * 3]; //可以定义成其他类型
glReadBuffer(GL_FRONT); //适合single buffer,如果用double的话,要GL_BACK
glPixelStorei(GL_PACK_ALIGNMENT,1); //具体原因是opengl本身的buffer格式和我们要用来写图像的长度不太一样
glReadPixels(0, 0, height, width, GL_RGB, GL_UNSIGNED_BYTE ,buffer); // 读出来是每个象素占3个字节(RGB)
......
5.直接读取framebuffer内容
由于android读取驱动的framebuffer内容的效率较低,在刷新比较快的时候,比如游戏画面,可能会由于刷新问题只截取到半张图或图片上下不匹配的问题。而且驱动的节点文件没有root权限没法访问,所以不推荐。
1.调用view的getDrawingCache接口,得到屏幕内容的bitmap;优点是使用方便,缺点是只能获取单个view的图像内容,如果此view被切换到后台,或者屏幕上还有别的view,那获取的内容都不是你在屏幕上看到的全屏的图像。
使用此方法先要通过setDrawingCacheEnable方法把cache开启,然后再调用getDrawingCache方法就可以获得view的cache图片了。buildDrawingCache方法可以不用调用,因为调用getDrawingCache方法时,若果cache没有建立,系统会自动调用buildDrawingCache方法生成cache。若果要更新cache, 必须要调用destoryDrawingCache方法把旧的cache销毁,才能建立新的。
当调用setDrawingCacheEnabled方法设置为false, 系统也会自动把原来的cache销毁。
2.调用系统截屏接口。android4.0以后系统自带截屏功能,使用方法是按下 power+volume - ,在同时按下2键并保持0.5s左右后,
会听到咔嚓一声响声,并弹出一个浮动动画,显示截图效果。我们在代码里可以模拟按键的方式来调用这个接口实现截屏,例如:
public void testScreenshot() throws Exception {
Log.d(LOG_TAG, "starting testScreenshot");
// launch the activity.
ScreenshotStubActivity activity = getActivity();
assertNotNull(activity);
File screenshotDir = getScreenshotDir();
NewScreenshotObserver observer = new NewScreenshotObserver(
screenshotDir.getAbsolutePath());
observer.startWatching();
takeScreenshot();
// unlikely, but check if a new screenshot file was already created
if (observer.getCreatedPath() == null) {
// wait for screenshot to be created
synchronized(observer) {
observer.wait(SCREEN_WAIT_TIME_SEC*1000);
}
}
assertNotNull(String.format("Could not find screenshot after %d seconds",
SCREEN_WAIT_TIME_SEC), observer.getCreatedPath());
File screenshotFile = new File(screenshotDir, observer.getCreatedPath());
try {
assertTrue(String.format("Detected new screenshot %s but its not a file",
screenshotFile.getName()), screenshotFile.isFile());
assertTrue(String.format("Detected new screenshot %s but its not an image",
screenshotFile.getName()), isValidImage(screenshotFile));
} finally {
// delete the file to prevent external storage from filing up
screenshotFile.delete();
}
}
private void takeScreenshot() {
getInstrumentation().sendKeySync(new KeyEvent(KeyEvent.ACTION_DOWN,
KeyEvent.KEYCODE_POWER));
getInstrumentation().sendKeySync(new KeyEvent(KeyEvent.ACTION_DOWN,
KeyEvent.KEYCODE_VOLUME_DOWN));
// the volume down key event will cause the 'volume adjustment' UI to appear in the
// foreground, and steal UI focus
// unfortunately this means the next key event will get directed to the
// 'volume adjustment' UI, instead of this test's activity
// for this reason this test must be signed with platform certificate, to grant this test
// permission to inject key events to another process
getInstrumentation().sendKeySync(new KeyEvent(KeyEvent.ACTION_UP,
KeyEvent.KEYCODE_VOLUME_DOWN));
getInstrumentation().sendKeySync(new KeyEvent(KeyEvent.ACTION_UP,
KeyEvent.KEYCODE_POWER));
}
此方法的实现的方法比较简单,代码流程如下:
2.1 按下 volume down+power;
2.2 PhoneWindowManager.java
里捕获按键,当VOLUME_DOWN和Power键被同时按下后,向SystemUI发送截屏Message;
2.3 SystemUI(TakeScreenshotService.java和GlobalScreenshot.java)收到来自Client端的截屏Message后,调用Surface.java的方法 screenshot()截屏,并将截取到的图片通过WindowManager以浮动窗口的形式显示给用户查看。screenshot()方法是@hide的,sdk里无法直接调用。
2.4 surface.java 调用JNI的android_view_Surface.cpp中的doScreenshot(...)方法,最终调用opengl的接口来获取屏幕图像:
-> [android_view_Surface.cpp] doScreenshot(...);
->update(...);
-> [SurfaceComposerClinet.cpp] update(...);
-> [SurfaceFlinger.cpp] captureScreen(...);
->captureScreenImplLocked(...);
->glReadPixels(...);
3.直接调用ComposerService的captureScreen接口,获取屏幕的rgb数据。此方法直接获取fb里的内容,是最灵活的,可实现屏幕录像;缺点是使用上最麻烦。示例代码如下:
frameworks\base\services\surfaceflinger\tests\screencap\screencap.cpp
#include <utils/Log.h>
#include <binder/IPCThreadState.h>
#include <binder/ProcessState.h>
#include <binder/IServiceManager.h>
#include <binder/IMemory.h>
#include <surfaceflinger/ISurfaceComposer.h>
#include <SkImageEncoder.h>
#include <SkBitmap.h>
using namespace android;
int main(int argc, char** argv)
{
if (argc != 2) {
printf("usage: %s path\n", argv[0]);
exit(0);
}
const String16 name("SurfaceFlinger");
sp<ISurfaceComposer> composer;
getService(name, &composer);
sp<IMemoryHeap> heap;
uint32_t w, h;
PixelFormat f;
status_t err = composer->captureScreen(0, &heap, &w, &h, &f, 0, 0);
if (err != NO_ERROR) {
fprintf(stderr, "screen capture failed: %s\n", strerror(-err));
exit(0);
}
printf("screen capture success: w=%u, h=%u, pixels=%p\n",
w, h, heap->getBase());
printf("saving file as PNG in %s ...\n", argv[1]);
SkBitmap b;
b.setConfig(SkBitmap::kARGB_8888_Config, w, h);
b.setPixels(heap->getBase());
SkImageEncoder::EncodeFile(argv[1], b, SkImageEncoder::kPNG_Type, SkImageEncoder::kDefaultQuality);
return 0;
}
如果想在你的apk里使用,需要添加权限:
<SPAN style="FONT-SIZE: 24px"><uses-permission android:name="android.permission.READ_FRAME_BUFFER" /></SPAN>
方法3实际上和方法2的原理一样,但是结果不一样,方法3可获取到rgb颜色数据,而不是图片。
4.使用opengl接口获取屏幕内容
4.1 java接口
示例代码如下:
public static Bitmap SavePixels(int x, int y, int w, int h, GL10 gl)
{
int b[]=new int[w*h];
int bt[]=new int[w*h];
IntBuffer ib=IntBuffer.wrap(b);
ib.position(0);
gl.glReadPixels(x, y, w, h, GL10.GL_RGBA, GL10.GL_UNSIGNED_BYTE, ib);
for(int i=0; i<h; i++)
{
for(int j=0; j<w; j++)
{
int pix=b[i*w+j];
int pb=(pix>>16)&0xff;
int pr=(pix<<16)&0x00ff0000;
int pix1=(pix&0xff00ff00) | pr | pb;
bt[(h-i-1)*w+j]=pix1;
}
}
Bitmap sb=Bitmap.createBitmap(bt, w, h, true);
return sb;
}
上面是一个简单的GL读取RGBA分量的方法,然后生成Android通用的Bitmap对象。
public static void SavePNG(int x, int y, int w, int h, String fileName, GL10 gl)
{
Bitmap bmp=SavePixels(x,y,w,h,gl);
try
{
FileOutputStream fos=new FileOutputStream("/sdcard/android123/"+fileName); //如何2.2或更高的系统sdcard路径为/mnt/sdcard/
bmp.compress(CompressFormat.PNG, 100, fos); //保存为png格式,质量100%
try
{
fos.flush();
}
catch (IOException e)
{
e.printStackTrace();
}
try
{
fos.close();
}
catch (IOException e)
{
e.printStackTrace();
}
}
catch (FileNotFoundException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
4.2 native接口
示例代码如下:
unsigned char* buffer[height * width * 3]; //可以定义成其他类型
glReadBuffer(GL_FRONT); //适合single buffer,如果用double的话,要GL_BACK
glPixelStorei(GL_PACK_ALIGNMENT,1); //具体原因是opengl本身的buffer格式和我们要用来写图像的长度不太一样
glReadPixels(0, 0, height, width, GL_RGB, GL_UNSIGNED_BYTE ,buffer); // 读出来是每个象素占3个字节(RGB)
......
5.直接读取framebuffer内容
由于android读取驱动的framebuffer内容的效率较低,在刷新比较快的时候,比如游戏画面,可能会由于刷新问题只截取到半张图或图片上下不匹配的问题。而且驱动的节点文件没有root权限没法访问,所以不推荐。
相关文章推荐
- android 截屏 和屏幕录像
- 在pc上通过socket从android手机上接收屏幕截屏
- android View的快照与屏幕的截屏
- Android 屏幕截屏方法汇总
- Android 系统截屏与系统内置DVR录像冲突,导致SystemUI重启的问题解决与分享
- android 屏幕录像
- Android 屏幕截屏,并将之生成视频文件
- Android安全性:屏蔽activity屏幕录制,截屏功能
- ubuntu下屏幕录像,截屏
- android屏幕录像
- android 关于屏幕截屏的几种办法
- android常见的屏幕工具,获取屏幕宽高,状态栏高,手机截屏(包括状态栏和不包括状态栏)
- vlc-android截屏以及录像功能的实现
- android 关于屏幕截屏的几种办法
- Android:screenrecord录制屏幕录像
- Android获取webView快照与屏幕截屏的方法
- Android系统自带屏幕录像
- Android 屏幕录像教程
- Android 屏幕录像解决方案
- cocos2dx-3.6 开发技巧(二)回调函数以及动画的使用,触摸事件,android返回键,屏幕截屏