谈谈Android中的SurfaceTexture
2015.7.2更新
由于很多人要代码,我把代码下载链接放在这里了。不过还是要说一下,surfaceTexture和OpenGL ES结合才能发挥出它最大的效果,我这种写法只是我自己的想法,还有很多种解决方法,如果大家学习一下OpenGL ES,会发现更多有意思的东西。
SurfaceTexture是从Android3.0(API 11)加入的一个新类。这个类跟SurfaceView很像,可以从camera preview或者video decode里面获取图像流(image stream)。但是,和SurfaceView不同的是,SurfaceTexture在接收图像流之后,不需要显示出来。有做过Android camera开发的人都知道,比较头疼的一个问题就是,从camera读取到的预览(preview)图像流一定要输出到一个可见的(Visible)SurfaceView上,然后通过Camera.PreviewCallback的public void onPreviewFrame(byte[] data, Camera camera)函数来获得图像帧数据的拷贝。这就存在一个问题,比如我希望隐藏摄像头的预览图像或者对每一帧进行一些处理再显示到手机显示屏上,那么在Android3.0之前是没有办法做到的,或者说你需要用一些小技巧,比如用其他控件把SurfaceView给挡住,注意这个显示原始camera图像流的SurfaceView其实是依然存在的,也就是说被挡住的SurfaceView依然在接收从camera传过来的图像,而且一直按照一定帧率去刷新,这是消耗cpu的,而且如果一些参数设置的不恰当,后面隐藏的SurfaceView有可能会露出来,因此这些小技巧并不是好办法。但是,有了SurfaceTexture之后,就好办多了,因为SurfaceTexture不需要显示到屏幕上,因此我们可以用SurfaceTexture接收来自camera的图像流,然后从SurfaceTexture中取得图像帧的拷贝进行处理,处理完毕后再送给另一个SurfaceView用于显示即可。
在我的应用场景中,我需要从camera读取图像流,然后对其处理,最后将处理结果显示到手机屏幕上。因此我写了一个GameDisplay类用于处理以上所有事务,而在MainActivity中,只需要创建一个GameDisplay类的实例并且初始化即可。
package com.song.camgame; import java.io.IOException;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask; import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.ImageFormat;
import android.graphics.Rect;
import android.graphics.SurfaceTexture;
import android.hardware.Camera;
import android.hardware.Camera.Size;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView; public class GameDisplay extends SurfaceView implements SurfaceHolder.Callback,
Camera.PreviewCallback{
public final static String TAG="GameDisplay"; private static final int MAGIC_TEXTURE_ID = 10;
public static final int DEFAULT_WIDTH=800;
public static final int DEFAULT_HEIGHT=480;
public static final int BLUR = 0;
public static final int CLEAR = BLUR + 1;
//public static final int PAUSE = PLAY + 1;
//public static final int EXIT = PAUSE + 1;
public SurfaceHolder gHolder;
public SurfaceTexture gSurfaceTexture;
public Camera gCamera;
public byte gBuffer[];
public int textureBuffer[];
public ProcessThread gProcessThread;
private int bufferSize;
private Camera.Parameters parameters;
public int previewWidth, previewHeight;
public int screenWidth, screenHeight;
public Bitmap gBitmap;
private Rect gRect;
// timer
private Timer sampleTimer;
private TimerTask sampleTask; public GameDisplay(Context context,int screenWidth,int screenHeight) {
super(context);
gHolder=this.getHolder();
gHolder.addCallback(this);
gHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
gSurfaceTexture=new SurfaceTexture(MAGIC_TEXTURE_ID);
this.screenWidth=screenWidth;
this.screenHeight=screenHeight;
gRect=new Rect(0,0,screenWidth,screenHeight);
Log.v(TAG, "GameDisplay initialization completed");
} @Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
Log.v(TAG, "GameDisplay surfaceChanged");
parameters = gCamera.getParameters();
List<Size> preSize = parameters.getSupportedPreviewSizes();
previewWidth = preSize.get(0).width;
previewHeight = preSize.get(0).height;
for (int i = 1; i < preSize.size(); i++) {
double similarity = Math
.abs(((double) preSize.get(i).height / screenHeight)
- ((double) preSize.get(i).width / screenWidth));
if (similarity < Math.abs(((double) previewHeight / screenHeight)
- ((double) previewWidth / screenWidth))) {
previewWidth = preSize.get(i).width;
previewHeight = preSize.get(i).height;
}
}
gBitmap= Bitmap.createBitmap(previewWidth, previewHeight, Bitmap.Config.ARGB_8888);
parameters.setPreviewSize(previewWidth, previewHeight);
gCamera.setParameters(parameters);
bufferSize = previewWidth * previewHeight;
textureBuffer=new int[bufferSize];
bufferSize = bufferSize * ImageFormat.getBitsPerPixel(parameters.getPreviewFormat()) / 8;
gBuffer = new byte[bufferSize];
gCamera.addCallbackBuffer(gBuffer);
gCamera.setPreviewCallbackWithBuffer(this);
gCamera.startPreview();
//gProcessThread = new ProcessThread(surfaceView,handler,null,previewWidth,previewHeight);
//processThread.start();
} @Override
public void surfaceCreated(SurfaceHolder holder) {
Log.v(TAG, "GameDisplay surfaceCreated");
if (gCamera == null) {
gCamera = Camera.open();
}
gCamera.setPreviewTexture(gSurfaceTexture);
//sampleStart();
} @Override
public void surfaceDestroyed(SurfaceHolder holder) {
Log.v(TAG, "GameDisplay surfaceDestroyed");
//gProcessThread.isRunning=false;
//sampleTimer.cancel();
//sampleTimer = null;
//sampleTask.cancel();
//sampleTask = null;
gCamera.stopPreview();
gCamera.release();
} @Override
public void onPreviewFrame(byte[] data, Camera camera) {
Log.v(TAG, "GameDisplay onPreviewFrame");
//gProcessThread.raw_data=data;
camera.addCallbackBuffer(gBuffer);
for(int i=0;i<textureBuffer.length;i++)
textureBuffer[i]=0xff000000|data[i];
gBitmap.setPixels(textureBuffer, 0, previewWidth, 0, 0, previewWidth, previewHeight);
synchronized (gHolder)
{
Canvas canvas = this.getHolder().lockCanvas();
canvas.drawBitmap(gBitmap, null,gRect, null);
//canvas.drawBitmap(textureBuffer, 0, screenWidth, 0, 0, screenWidth, screenHeight, false, null);
this.getHolder().unlockCanvasAndPost(canvas);
} } public void sampleStart() {
Log.v(TAG, "GameDisplay sampleStart");
sampleTimer = new Timer(false);
sampleTask = new TimerTask() {
@Override
public void run() {
gProcessThread.timer=true;
}
};
sampleTimer.schedule(sampleTask,0, 80);
}
}
以上程序中115-130行是最重要的部分,data是从SurfaceTexture获得的camera图像帧的拷贝,119-121行对其进行了简单的处理,122-128行将处理过的图像数据传递给负责显示的SurfaceView并显示出来。
MainActivity对GameDisplay的调用如下:
//声明
private GameDisplay gameDisplay;
//初始化
gameDisplay.setVisibility(SurfaceView.VISIBLE);
DisplayMetrics dm = getResources().getDisplayMetrics();
screenWidth = dm.widthPixels;
screenHeight = dm.heightPixels;
gameDisplay= new GameDisplay(this,dm.widthPixels,dm.heightPixels);
//加入到当前activity的layout中
FrameLayout root = (FrameLayout) findViewById(R.id.root);
root.addView(gameDisplay,0);
谈谈Android中的SurfaceTexture的更多相关文章
- 谈谈Android中的Rect类——奇葩的思维
最近在工作中遇到了一些问题,总结下来就是Android中Rect这个类造成的.不得不说,不知道Android SDK的开发人员是怎么想的, 这个类设计的太奇葩了.首先介绍一下Rect类:Rect类主要 ...
- (转)谈谈Android中的Rect类——奇葩的思维
最近在工作中遇到了一些问题,总结下来就是Android中Rect这个类造成的.不得不说,不知道Android SDK的开发人员是怎么想的, 这个类设计的太奇葩了.首先介绍一下Rect类:Rect类主要 ...
- Android中线程和线程池
我们知道线程是CPU调度的最小单位.在Android中主线程是不能够做耗时操作的,子线程是不能够更新UI的.在Android中,除了Thread外,扮演线程的角色有很多,如AsyncTask,Inte ...
- 访何红辉:谈谈Android源码中的设计模式
最近Android 6.0版本的源代码开放下载,刚好分析Android源码的技术书籍<Android源码设计模式解析与实战>上市,我们邀请到它的作者何红辉,来谈谈Android源码中的设计 ...
- Android 5.0(Lollipop)中的SurfaceTexture,TextureView, SurfaceView和GLSurfaceView
SurfaceView, GLSurfaceView, SurfaceTexture以及TextureView是Android当中名字比较绕,关系又比较密切的几个类.本文基于Android 5.0(L ...
- 谈谈对Android中的消息机制的理解
Android中的消息机制主要由Handler.MessageQueue.Looper三个类组成,他们的主要作用是 Handler负责发送.处理Message MessageQueue负责维护Mess ...
- Android中使用ImageViewSwitcher实现图片切换轮播导航效果
前面写过了使用ViewFlipper和ViewPager实现屏幕中视图切换的效果(ViewPager未实现轮播)附链接: Android中使用ViewFlipper实现屏幕切换 Android中使用V ...
- Android中后台的劳动者“服务”
前言 作为四大组件之一的Service,想必不少开发者都是了解的,那具体熟悉吗?是不是对Service中的每个知识点是否了解,它与Activity的关系又是什么样的,我们所理解的后台服务跟Servic ...
- 谈谈Android 6.0运行时权限理解
前言 谷歌在2015年8月份时候,发布了Android 6.0版本,代号叫做“棉花糖”(Marshmallow ),其中的很大的一部分变化,是在用户权限授权上,或许是感觉之前默认授权的不合理,现在6. ...
随机推荐
- Eigen教程(8)
整理下Eigen库的教程,参考:http://eigen.tuxfamily.org/dox/index.html 原生缓存的接口:Map类 这篇将解释Eigen如何与原生raw C/C++ 数组混合 ...
- iOSAFNetworking 网络请求
前言 在 iOS 开发中,一般情况下,简单的向某个 Web 站点简单的页面提交请求并获取服务器的响应,用 Xcode 自带的 NSURLConnection 是能胜任的.但是,在绝大部分下我们所需要访 ...
- Android Studio SVN使用
昨天弄了一天的Android Studio svn,感觉没有eclipse的svn好装,中间遇到很多的麻烦问题.这里来记录下吧 上传比较简单,就直接贴我看我别的的上传教程. https://blog. ...
- JD 题目1040:Prime Number (筛法求素数)
OJ题目:click here~~ 题目分析:输出第k个素数 贴这么简单的题目,目的不清纯 用筛法求素数的基本思想是:把从1開始的.某一范围内的正整数从小到大顺序排列, 1不是素数,首先把它筛掉.剩下 ...
- Centos6.5生成环境配置--nginx1.9 + PHP+可多个tomcat(多个端口)+多域名+java web 负载均衡
安装n p 参考: CentOS6.5搭建LNMP http://www.cnblogs.com/xiaoit/p/3991037.html http://blog.csdn.net/keyunq/a ...
- php设计模式总结-单件模式
一.单件模式 英文叫做sington.其他语言中有叫做单例模式,其实都是一样的道理.保证只会出现单个实例,所以是单例.翻译成单件,永远只会产生一件,呵呵. 还有翻译成单元素模式.其实关键是看这个英文比 ...
- velocity单引号与双引号
(1)最外层是用单引号包围时,双引号直接使用就可以了,两个连续的单引号表示一个单引号:#set($var2 = 'A"B''C') --> $var2 的值为 A"B'C(2 ...
- Medium开发团队谈架构设计_转
转自:Medium开发团队谈架构设计 背景 说到底,Medium是个社交网络,人们可以在这里分享有意思的故事和想法.据统计,目前累积的用户阅读时间已经超过14亿分钟,合两千六百年. 我们支持着每个月两 ...
- drupal 精彩文章
1.如何快速查找Drupal表单的Form ID?http://www.drupalla.com/node/2306
- [算法]最小的K个数和数据流中的中位数
1. 最小的K个数 题目描述 输入n个整数,找出其中最小的K个数.例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4. 思路 Java 中的PriorityQueue是 ...